React Integration

Render Apple Pay and Google Pay in your own checkout

Overview

The CrissCross wallets SDK renders Apple Pay and Google Pay buttons inside your own checkout, on your own domain, while CrissCross keeps the wallet plumbing: merchant validation, token decryption, 3-D Secure, and the transaction itself.

The SDK is initialised with a sessionId and nothing else. CrissCross resolves the wallet identifiers, the accepted card networks and the merchant display name for that session on the server, exactly as it does for Secure Fields.

There is no API key in your front-end bundle. Your OAuth client_id and client_secret are server-side credentials and must never reach the browser — the SDK does not accept them and does not need them. A sessionId authorises exactly one checkout and nothing else.

Pre-requisites

  • Apple Pay and Google Pay enabled on your account.
  • Your payment domains registered and verified. See Wallet Configuration.
  • A checkout session created server-side. Create it with integrationType: "direct", since you are rendering the checkout yourself.

Installation

npm install @crisscross/wallets

Creating the session

Create the session from your server, as you would for any CrissCross payment, and pass the sessionId to your front-end. Never create sessions from the browser — session creation uses your OAuth credentials.

curl -X POST https://api.crisscross.money/v1/checkout/session \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"merchantId": "YOUR_MERCHANT_ID",
"merchantReference": "ORDER67890",
"amount": 20000,
"currency": "ZAR",
"integrationType": "direct",
"redirectUrl": "https://merchant.com/redirect",
"payerDetails": {
"emailAddress": "[email protected]",
"location": "ZAF",
"fullName": "Siyanda Mthembu",
"phoneNumber": "0723456789"
}
}'

Rendering the buttons

Wrap your checkout in <CrissCrossWallets> and drop in the buttons you want to offer. The provider resolves the session’s wallet configuration; each button renders only if the customer’s device and browser can present that wallet and a card is provisioned.

import {
CrissCrossWallets,
ApplePayButton,
GooglePayButton,
} from '@crisscross/wallets';
function Checkout({ sessionId }) {
return (
<CrissCrossWallets sessionId={sessionId}>
<ApplePayButton
onSuccess={({ transactionId }) => confirmOrder(transactionId)}
onError={(error) => showRetry(error)}
/>
<GooglePayButton
onSuccess={({ transactionId }) => confirmOrder(transactionId)}
onError={(error) => showRetry(error)}
/>
</CrissCrossWallets>
);
}

A button that cannot be presented renders nothing at all — it does not render disabled. Lay out your checkout so that an absent wallet button leaves no gap, and always offer at least one other payment method.

Knowing what is available before you render

To branch on availability yourself — to choose a heading, or to reorder your payment options — use the useWallets hook:

import { useWallets } from '@crisscross/wallets';
function PaymentOptions() {
const { available, loading } = useWallets();
if (loading) return <Spinner />;
return (
<>
{available.length > 0 && <h2>Express checkout</h2>}
{available.includes('apple-pay') && <ApplePayButton onSuccess={...} />}
{available.includes('google-pay') && <GooglePayButton onSuccess={...} />}
</>
);
}

available combines two things: what the session permits, from GET /v1/payment/available-methods, and what the customer’s device can actually present. Only wallets satisfying both appear.

What happens when the customer pays

  1. The customer taps the button and the wallet sheet opens on their device.
  2. They authorise with Face ID, Touch ID, a passcode, or a screen lock.
  3. The device releases an encrypted, single-use payment token, which the SDK submits to CrissCross. For Apple Pay the SDK also completes the merchant validation round trip; you do not implement it.
  4. If 3-D Secure is required, the SDK presents the challenge and resolves it before returning. Most wallet payments are exempt, because the device authentication already satisfies strong customer authentication.
  5. onSuccess fires with the transactionId.

A customer who dismisses the wallet sheet without authorising produces no transaction and no callback. Leave your checkout as it was.

Confirm on the webhook, not on the callback

onSuccess tells you the wallet authorised and CrissCross accepted the payment. It is a display signal, exactly like the hosted checkout’s ?status=completed redirect. Show a confirmation from it — but fulfil the order from the webhook, which is the authoritative outcome and arrives even if the customer closes the tab. See Webhook Events.

Match the webhook to your order on sessionId or merchantReference.

Handling errors

onError receives a failure that has already been resolved as far as it can be:

SituationWhat to do
Payment declinedShow a retry and keep the other payment methods available. The session is still usable
3-D Secure failed or was abandonedSame as a decline — the customer can try again
Session expiredCreate a new session and re-render
Wallet unavailableNever surfaced as an error. The button simply does not render

Do not retry automatically. A declined wallet payment usually needs the customer to pick a different card in the wallet sheet.

Vanilla JavaScript

The same functionality is available without React for other frameworks:

import { createWalletSession } from '@crisscross/wallets';
const wallets = await createWalletSession({ sessionId });
if (wallets.available.includes('apple-pay')) {
wallets.mount('apple-pay', '#apple-pay-container', {
onSuccess: ({ transactionId }) => confirmOrder(transactionId),
onError: (error) => showRetry(error),
});
}

Additional resources