USD Card Payments
USD card payments use the same S2S card initialize endpoint and the same encrypted card flow as standard card payments. To process a USD card payment, set currency to USD.
USD card collection must be enabled for the merchant’s business before USD card payments can be processed. When USD collection is enabled, USD card payments currently support Visa and Mastercard.
Visa and Mastercard use the same request payloads, encryption flow, initialize endpoint, OTP completion endpoint, 3DS completion endpoint, and verification endpoint.
Card details must be encrypted before calling initialize. Never send raw card details to the initialize endpoint, and never store raw card number, CVV, PIN, or expiry.
Some USD card payments may complete immediately. Some may require OTP, and some may require 3DS authentication. The required authentication method is returned in the initialize response.
Requirements
| Requirement | Description |
|---|---|
| currency | Must be USD |
| Supported cards | Visa and Mastercard |
| Business activation | USD collection must be enabled for the business |
| Card security | Card details must be encrypted before initialize |
| Authentication | SaySwitch may return success, failure, OTP required, or 3DS required |
USD Card Flow
- Encrypt card details using the existing S2S card encryption endpoint.
- Call the normal S2S card initialize endpoint with
currency: "USD". - Check the response.
- If payment is successful, no further action is required.
- If payment failed, show the failure message.
- The merchant does not choose OTP or 3DS manually. After the initialize request, SaySwitch returns the required next action. Inspect
data.authto determine the next step. - If
data.authis"otp", complete OTP authorization. - If
data.authis"3ds", complete the 3DS browser challenge and then call the 3DS completion endpoint. - Verify final transaction status using the existing verify transaction endpoint.
Legacy S2S Endpoints
| Action | Method | Endpoint |
|---|---|---|
| Initialize card payment | POST | https://backendapi.sayswitchgroup.com/api/s2s/transaction/initialize |
| Complete OTP authorization | POST | https://backendapi.sayswitchgroup.com/api/s2s/transaction/card/otp |
| Complete 3DS authorization | POST | https://backendapi.sayswitchgroup.com/api/s2s/transaction/card/3ds |
| Verify transaction | GET | https://backendapi.sayswitchgroup.com/api/s2s/transaction/verify/{reference} |
S2S v1 Endpoints
The /api/s2s/v1 endpoints follow the same request and response structure.
| Action | Method | Endpoint |
|---|---|---|
| Initialize card payment | POST | https://backendapi.sayswitchgroup.com/api/s2s/v1/transaction/initialize |
| Complete OTP authorization | POST | https://backendapi.sayswitchgroup.com/api/s2s/v1/transaction/card/otp |
| Complete 3DS authorization | POST | https://backendapi.sayswitchgroup.com/api/s2s/v1/transaction/card/3ds |
| Verify transaction | GET | https://backendapi.sayswitchgroup.com/api/s2s/v1/transaction/verify/{reference} |
Step 1: Encrypt USD Card Details
This is the same encryption process already documented for card payments.
Example Raw Card Payload Before Encryption
{
"data": {
"number": "5555555555554444",
"expiryMonth": "12",
"expiryYear": "28",
"cvv": "123"
},
"reference": "USD_REF_2026_001"
}If PIN is required by the card flow, include it inside the encrypted card data:
{
"data": {
"number": "5555555555554444",
"expiryMonth": "12",
"expiryYear": "28",
"cvv": "123",
"pin": "1234"
},
"reference": "USD_REF_2026_001"
}Fields
| Field | Type | Required | Description |
|---|---|---|---|
| data.number | string | Yes | Card number |
| data.expiryMonth | string | Yes | Card expiry month in MM format |
| data.expiryYear | string | Yes | Card expiry year in YY format |
| data.cvv | string | Yes | Card security code |
| data.pin | string | No | Card PIN, only if required |
| reference | string | Yes | Unique encryption reference. Use the same reference when initializing the transaction |
Encryption Success Response
{
"status": true,
"message": "Card encrypted successfully",
"data": {
"encryptedCard": "ENCRYPTED_CARD_HEX_STRING",
"reference": "USD_REF_2026_001"
}
}Save the encryptedCard value temporarily for this payment session. It is needed for initialize and may also be needed again if 3DS authorization is required.
Step 2: Initialize USD Card Payment
Endpoint
POST https://backendapi.sayswitchgroup.com/api/s2s/transaction/initializeHeaders
{
"Content-Type": "application/json",
"Authorization": "Bearer sk_test_your_secret_key_here"
}Request Body
{
"amount": "10.00",
"card": "ENCRYPTED_CARD_HEX_STRING",
"currency": "USD",
"email": "customer@example.com",
"reference": "USD_REF_2026_001",
"callbackUrl": "https://merchant.example.com/payment/callback"
}cURL Example
curl --location 'https://backendapi.sayswitchgroup.com/api/s2s/transaction/initialize' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer sk_test_your_secret_key_here' \
--data-raw '{
"amount": "10.00",
"card": "ENCRYPTED_CARD_HEX_STRING",
"currency": "USD",
"email": "customer@example.com",
"reference": "USD_REF_2026_001",
"callbackUrl": "https://merchant.example.com/payment/callback"
}'Fields
| Field | Type | Required | Description |
|---|---|---|---|
| amount | string | Yes | Payment amount |
| card | string | Yes | Encrypted card value returned from the encryption step |
| currency | string | Yes | Use USD for USD card payments |
| string | Yes | Customer email address | |
| reference | string | Yes | Unique transaction reference |
| callbackUrl | string | Yes | Merchant callback or return URL used to return the customer after 3DS authentication. |
Do not send wallet_code to the default initialize endpoint.
For USD card payments, callbackUrl is required because some cards may require 3DS authentication. The customer will be returned to this URL after completing authentication.
Step 3: Handle Initialize Response
The initialize response determines the next step. Inspect the response and act based on the returned status and data.auth value.
Possible Response A: Payment Successful
{
"status": true,
"message": "Payment Successful"
}No further authentication is required. You may verify the transaction status using the verify endpoint.
Possible Response B: Payment Failed
{
"status": false,
"message": "Transaction Failed"
}Display the failure message to the customer or ask the customer to try another card.
Possible Response C: OTP Authorization Required
{
"status": true,
"message": "OTP authorization required",
"data": {
"auth": "otp",
"otp": {
"message": "Please enter the OTP sent to the cardholder",
"tokenId": "TOKEN_ID"
}
},
"_links": {
"url": "https://backendapi.sayswitchgroup.com/api/s2s/transaction/card/otp",
"method": "POST",
"payload": ["ref", "otp"]
}
}| Field | Description |
|---|---|
| data.auth | Shows the required authentication type. For OTP, value is otp |
| data.otp.message | Message to show the customer |
| data.otp.tokenId | OTP token reference returned for the payment |
| _links.url | Endpoint to complete OTP authorization |
| _links.method | HTTP method for the OTP completion request |
| _links.payload | Required fields for the OTP completion request |
Ask the customer to enter the OTP and call the OTP completion endpoint.
Possible Response D: 3DS Authorization Required
{
"status": true,
"message": "3DS authorization required",
"data": {
"auth": "3ds",
"threed": {
"id": "3DS_AUTH_ID",
"redirectUrl": "https://acs-bank-url.example.com",
"acsUrl": "https://acs-bank-url.example.com",
"method": "POST",
"payload": {
"creq": "C_REQ_VALUE",
"TermUrl": "https://merchant.example.com/payment/return?reference=USD_REF_2026_001"
},
"termUrl": "https://merchant.example.com/payment/return?reference=USD_REF_2026_001",
"callBackUrl": "https://secure-callback-url.example.com",
"orderId": "ORDER_ID",
"transactionId": "TRANSACTION_ID",
"paymentId": "PAYMENT_ID"
}
},
"_links": {
"url": "https://backendapi.sayswitchgroup.com/api/s2s/transaction/card/3ds",
"method": "POST",
"payload": ["ref", "card"]
}
}| Field | Description |
|---|---|
| data.auth | Shows the required authentication type. For 3DS, value is 3ds |
| data.threed.acsUrl | ACS URL where the customer should complete 3DS authentication |
| data.threed.redirectUrl | Redirect URL for 3DS authentication. Use this if acsUrl is not available |
| data.threed.method | HTTP method to use when submitting customer to ACS |
| data.threed.payload | Payload to submit to ACS |
| data.threed.termUrl | URL used after 3DS authentication |
| _links.url | Endpoint to complete 3DS authorization after the customer authenticates |
| _links.method | HTTP method for the 3DS completion request |
| _links.payload | Required fields for the 3DS completion request |
Submit the customer to acsUrl or redirectUrl using the returned method and all values in payload. The customer is returned to the merchant’s callbackUrl after completing the 3DS challenge. From that callback/return page, call the 3DS completion endpoint with the transaction reference and encrypted card.
3DS Display Options
When data.auth is 3ds, your application must present the ACS challenge to the customer.
You can do this by either:
- redirecting the customer to
data.threed.acsUrlordata.threed.redirectUrl, or - submitting the ACS form into an iframe/modal inside your checkout page.
If you use an iframe, submit all fields in data.threed.payload as hidden form inputs and set the form target to the iframe name.
After the customer completes authentication and returns to your callbackUrl, call the 3DS completion endpoint.
Step 4A: Complete OTP Authorization
Endpoint
POST https://backendapi.sayswitchgroup.com/api/s2s/transaction/card/otpHeaders
{
"Content-Type": "application/json",
"Authorization": "Bearer sk_test_your_secret_key_here"
}Request Body
{
"ref": "USD_REF_2026_001",
"otp": "123456"
}Optional Request Body If tokenId Is Needed
{
"ref": "USD_REF_2026_001",
"otp": "123456",
"tokenId": "TOKEN_ID"
}cURL Example
curl --location 'https://backendapi.sayswitchgroup.com/api/s2s/transaction/card/otp' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer sk_test_your_secret_key_here' \
--data-raw '{
"ref": "USD_REF_2026_001",
"otp": "123456"
}'Fields
| Field | Type | Required | Description |
|---|---|---|---|
| ref | string | Yes | Transaction reference from initialize request |
| otp | string | Yes | OTP entered by customer |
| tokenId | string | No | OTP token ID returned from initialize response, if available |
Possible OTP Success Response
{
"status": true,
"message": "Payment Successful"
}Possible OTP Failed Response
{
"status": false,
"message": "Invalid OTP or payment could not be completed"
}Step 4B: Complete 3DS Authorization
Endpoint
POST https://backendapi.sayswitchgroup.com/api/s2s/transaction/card/3dsHeaders
{
"Content-Type": "application/json",
"Authorization": "Bearer sk_test_your_secret_key_here"
}Request Body
{
"ref": "USD_REF_2026_001",
"card": "ENCRYPTED_CARD_HEX_STRING"
}cURL Example
curl --location 'https://backendapi.sayswitchgroup.com/api/s2s/transaction/card/3ds' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer sk_test_your_secret_key_here' \
--data-raw '{
"ref": "USD_REF_2026_001",
"card": "ENCRYPTED_CARD_HEX_STRING"
}'Fields
| Field | Type | Required | Description |
|---|---|---|---|
| ref | string | Yes | Transaction reference from initialize request |
| card | string | Yes | The encrypted card value from the encryption step |
The encrypted card value is required again for 3DS completion. Keep it only temporarily for the active payment session. Do not store raw card details.
Possible 3DS Success Response
{
"status": true,
"message": "Payment Successful"
}Possible 3DS Failed Response
{
"status": false,
"message": "Error processing request, card transaction is either complete or does not require 3DS."
}Step 5: Verify Transaction
Endpoint
GET https://backendapi.sayswitchgroup.com/api/s2s/transaction/verify/{reference}S2S v1 Endpoint
GET https://backendapi.sayswitchgroup.com/api/s2s/v1/transaction/verify/{reference}Use the verify endpoint version that matches the S2S initialize endpoint version used for the payment.
Example Request
GET https://backendapi.sayswitchgroup.com/api/s2s/transaction/verify/USD_REF_2026_001Headers
{
"Authorization": "Bearer sk_test_your_secret_key_here"
}Success Response Example
{
"success": true,
"message": "Verification successful",
"data": {
"amount": "10.00",
"currency": "USD",
"status": "success",
"reference": "USD_REF_2026_001",
"channel": "card",
"fees": "0.12",
"requested_amount": "10.00"
}
}Transaction Statuses
| Status | Description |
|---|---|
| success | Payment completed successfully |
| pending | Payment is still processing or awaiting authentication |
| failed | Payment failed or was declined |
Complete USD Card Integration Example
const encryptedCard = await encryptCardDetails({
number: "5555555555554444",
expiryMonth: "12",
expiryYear: "28",
cvv: "123"
}, "USD_REF_2026_001");
const paymentResponse = await fetch("https://backendapi.sayswitchgroup.com/api/s2s/transaction/initialize", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer sk_test_your_secret_key_here"
},
body: JSON.stringify({
amount: "10.00",
card: encryptedCard,
currency: "USD",
email: "customer@example.com",
reference: "USD_REF_2026_001",
callbackUrl: "https://merchant.example.com/payment/callback"
})
});
const paymentData = await paymentResponse.json();
if (paymentData.status === true && paymentData.message === "Payment Successful") {
// Payment completed
}
if (paymentData.status === false) {
// Show paymentData.message to the customer
}
if (paymentData.data?.auth === "otp") {
// Collect OTP from customer
const otp = "123456";
await fetch(paymentData._links.url, {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer sk_test_your_secret_key_here"
},
body: JSON.stringify({
ref: "USD_REF_2026_001",
otp
})
});
}
if (paymentData.data?.auth === "3ds") {
const threed = paymentData.data.threed;
// Submit customer to threed.acsUrl or threed.redirectUrl
// using threed.method and threed.payload.
// After customer completes authentication and returns,
// call the 3DS completion endpoint.
await fetch(paymentData._links.url, {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer sk_test_your_secret_key_here"
},
body: JSON.stringify({
ref: "USD_REF_2026_001",
card: encryptedCard
})
});
}
const verifyResponse = await fetch("https://backendapi.sayswitchgroup.com/api/s2s/transaction/verify/USD_REF_2026_001", {
method: "GET",
headers: {
"Authorization": "Bearer sk_test_your_secret_key_here"
}
});
const verification = await verifyResponse.json();
if (verification?.data?.status === "success") {
// Mark payment as successful
} else if (verification?.data?.status === "failed") {
// Mark payment as failed
} else {
// Treat as pending and retry verification later
}Final Notes
- The merchant should not assume every USD card requires OTP or 3DS.
- The initialize response determines the next step.
- Always inspect
data.auth. - Keep
encryptedCardtemporarily until the payment is completed. - Do not expose or store raw card data.