Non-3DS transactions
Process an online card transaction without 3DS.
Overview
By implementing a non-3DS payment flow, you benefit from:
- Faster checkout: Streamlined payment process with fewer steps and redirects.
- Better conversion: Reduced friction leads to higher completion rates.
- Simplified integration: Fewer callbacks and less complex implementation.
- Lower latency: Direct payment processing without authentication steps.
However, non-3DS payments typically have higher risk and may not qualify for liability shift protection. They're best suited for low-risk transactions, trusted customers, or when 3DS authentication isn't required.
Payment flow
The non-3DS payment flow is a streamlined process consisting of five key steps.
Step 1: Submission
The customer clicks the submit button or the payment is triggered programmatically. The submitAsync()
method is invoked and validation occurs inside it. If validation passes, the method continues with the payment processing. If it fails, the method exits early and doesn't proceed with the transaction.
Step 2: Card tokenisation
If it's a new card, the SDK sends the card details to the tokenisation service. If it's a saved card, the SDK retrieves the existing token.
Step 3: Evaluation
The SDK evaluates whether 3DS authentication is required. In this flow, 3DS is not required based on factors such as:
- The transaction intent being
Payout
. - Your configuration not requiring 3DS authentication (i.e.,
onPreInitiateAuthentication
callback not provided). - The transaction falling below risk thresholds.
- Regulatory exemptions being applicable.
Since 3DS is not required, the flow skips all authentication steps and proceeds directly to authorisation.
Step 4: Authorisation
This is the final processing step where the SDK sends the authorisation request directly to the payment gateway without any 3DS authentication data. The transaction data is processed using standard card payment protocols.
The authorisation step has two associated callbacks:
onPreAuthorisation
: Provides transaction data for final review. This is your last chance to modify the transaction before authorisation. Note that in non-3DS flows, no authentication results are included.onPostAuthorisation
: Receives the final transaction result from the payment gateway. The transaction is either approved or declined with standard payment processing details.
Step 5: Authorisation result
You receive the final authorisation response from the payment gateway. The transaction is either approved or declined and final transaction details are available. Since this is a non-3DS flow, no authentication confirmation data is included in the response.
Implementation
Before you start
To use non-3DS payments in your application:
- Ensure your merchant account supports non-3DS transactions.
- Configure your risk settings appropriately in the Unity Portal.
- Consider implementing additional fraud prevention measures.
Step 1: Configure your SDK
Set up your sdkConfig
with basic transaction information.
const sdkConfig = {
transactionData: {
amount: 99.99,
currency: 'USD',
entryType: 'Ecom',
intent: 'Authorisation',
merchantTransactionId: 'order-123',
merchantTransactionDate: () => new Date().toISOString(),
// Optional shopper data for enhanced processing
shopper: {
email: '[email protected]',
firstName: 'John',
lastName: 'Doe'
}
},
// Your other SDK configuration
};
Step 2: Implement callbacks
Implement the required callbacks for non-3DS payments.
const cardSubmitComponent = new CardSubmitComponent(sdkConfig, {
// REQUIRED: Final transaction approval
onPreAuthorisation: async (preAuthData, threeDSData) => {
console.log('Processing non-3DS transaction:', preAuthData);
// Add any additional transaction data
return {
...preAuthData.transactionInitiationData,
// Add custom metadata if needed
metadata: {
processingType: 'non-3ds',
timestamp: new Date().toISOString()
}
};
},
// REQUIRED: Handle the final result
onPostAuthorisation: (result) => {
if (result.success) {
console.log('Payment successful!');
console.log('Transaction ID:', result.transactionId);
// Redirect to success page
window.location.href = '/payment-success';
} else {
console.log('Payment failed:', result.errorMessage);
// Handle failure
showError('Payment failed. Please try again.');
}
},
// OPTIONAL: Handle errors during processing
onSubmitError: (error) => {
console.error('Payment error:', error);
showError('Payment failed. Please try again.');
}
});
Step 3: Handle common scenarios
Amount-based processing
Use different processing based on transaction amounts.
const cardSubmitComponent = new CardSubmitComponent(sdkConfig, {
onPreAuthorisation: async (preAuthData, threeDSData) => {
const amount = sdkConfig.transactionData.amount;
// Add risk indicators for higher amounts
if (amount > 100) {
return {
...preAuthData.transactionInitiationData,
riskData: {
level: 'medium',
reason: 'high-value-transaction'
}
};
}
return preAuthData.transactionInitiationData;
}
});
Customer type handling
Handle different customer types with varying risk profiles.
const getCustomerRiskProfile = (customerType) => {
switch (customerType) {
case 'new':
return { level: 'high', verification: 'enhanced' };
case 'returning':
return { level: 'medium', verification: 'standard' };
case 'vip':
return { level: 'low', verification: 'minimal' };
default:
return { level: 'medium', verification: 'standard' };
}
};
const cardSubmitComponent = new CardSubmitComponent(sdkConfig, {
onPreAuthorisation: async (preAuthData, threeDSData) => {
const riskProfile = getCustomerRiskProfile('returning');
return {
...preAuthData.transactionInitiationData,
customerRisk: riskProfile
};
}
});
Step 4: Handle errors
Implement comprehensive error handling for non-3DS payments.
const cardSubmitComponent = new CardSubmitComponent(sdkConfig, {
onSubmitError: (error) => {
console.error('Payment error:', error);
// Handle specific error types
if (error.code === 'INSUFFICIENT_FUNDS') {
showError('Insufficient funds. Please try a different payment method.');
} else if (error.code === 'CARD_DECLINED') {
showError('Your card was declined. Please try a different payment method.');
} else if (error.code === 'INVALID_CARD') {
showError('Invalid card details. Please check and try again.');
} else if (error.code === 'NETWORK_ERROR') {
showError('Connection failed. Please check your internet and try again.');
} else {
showError('Payment failed. Please try again.');
}
},
onPostAuthorisation: (result) => {
if (!result.success) {
// Handle different decline reasons
const declineCode = result.stateData?.code;
switch (declineCode) {
case '05': // Do not honor
showError('Payment declined by your bank. Please try a different card.');
break;
case '14': // Invalid card number
showError('Invalid card number. Please check your details.');
break;
case '54': // Expired card
showError('Your card has expired. Please use a different card.');
break;
case '61': // Exceeds withdrawal limit
showError('Transaction exceeds your card limit. Try a smaller amount.');
break;
default:
showError('Payment was declined. Please try a different payment method.');
}
}
}
});
Example
The following example shows a simple non-3DS implementation using the card submit component.
const cardSubmitComponent = new CardSubmitComponent(sdkConfig, {
// Step 1: Process the transaction
onPreAuthorisation: async (preAuthData, threeDSData) => {
console.log('Processing card payment');
// Log transaction details
console.log(`Amount: ${preAuthData.transactionInitiationData.amounts.transaction}`);
console.log(`Currency: ${preAuthData.transactionInitiationData.amounts.currencyCode}`);
// Add any additional data
return {
...preAuthData.transactionInitiationData,
processingNotes: 'Non-3DS direct payment'
};
},
// Step 2: Handle success/failure
onPostAuthorisation: (result) => {
if (result.success) {
// Success - redirect to confirmation
console.log('Payment completed successfully');
sessionStorage.setItem('paymentSuccess', 'true');
window.location.href = `/payment-success?tx=${result.transactionId}`;
} else {
// Failure - show error and allow retry
console.error('Payment failed:', result.errorMessage);
showError('Payment failed: ' + (result.errorMessage || 'Please try again'));
enableRetryButton();
}
},
// Step 3: Error handling
onSubmitError: (error) => {
console.error('Payment error:', error);
hideLoadingSpinner();
showError('Payment failed. Please try again.');
}
});
Callback data
This section describes the data received by the different callbacks as part of the non-3DS flow.
onPreAuthorisation
The onPreAuthorisation
callback receives:
- Pre-authorisation data (
preAuthData
): Transaction data ready for authorisation. - 3DS data (
threeDSData
): This will benull
orundefined
for non-3DS flows.
Pre-authorisation data
The pre-authorisation data includes transaction initiation data (for new cards) or card token data (for saved cards).
{
initiateIntegratedSuccessAuthenticationResult: null,
transactionInitiationData: {
threeDSecureData: null,
psd2Data: {
scaExemption: "LowValue"
},
identityVerification: {
nameVerification: true
},
addressVerification: {
countryCode: "US",
houseNumberOrName: "123",
postalCode: "10001"
}
},
cardTokenData: null
}
{
initiateIntegratedSuccessAuthenticationResult: null,
transactionInitiationData: null,
cardTokenData: {
gatewayTokenId: "gw_token_abc123def456789",
schemeTokenId: null,
maskedPrimaryAccountNumber: "****-****-****-4242",
cardExpiryMonth: "12",
cardExpiryYear: "2025",
scheme: "VISA",
fundingSource: "CREDIT",
ownerType: "PERSONAL",
issuerName: "Chase Bank",
issuerCountryCode: "US",
lastSuccessfulPurchaseDate: "2024-01-15T10:30:00Z",
lastSuccessfulPayoutDate: null
}
}
Parameter | Description |
---|---|
| This is always |
| Details about the transaction, if associated with a new card, |
| This is always |
| Details about PSD2. This is required for non-3DS transactions. |
| The type of SCA exemption that applies to this transaction.
|
| Details about the identity verification. |
| Whether the cardholder's name matches the name associated with the registered address on file. |
| Details about the address verification. |
| The country code associated with the cardholder's address, in ISO-3166-1 alpha-2 format. |
| The cardholder's street address. |
| The postal or ZIP code associated with the cardholder's address. |
| Details about the card token if associated with a saved card, |
| The gateway token ID. |
| The scheme token ID. |
| The masked Primary Account Number (PAN). |
| The expiry month ( |
| The expiry year ( |
| The card scheme. |
| The funding source. |
| The owner type. |
| The issuer name. |
| The country code of the issuer. |
| The date of the last successful purchase. |
| The date of the last successful payout. |
Here's an example of what to do with this data:
onPreAuthorisation: async (preAuthData, threeDSData) => {
console.log('Card transaction data:', preAuthData);
if (threeDSData) {
console.warn('Unexpected 3DS data in non-3DS flow');
}
const transactionData = preAuthData.transactionInitiationData;
// Add fraud prevention data
const enhancedData = {
...transactionData,
// Add browser fingerprinting
deviceData: {
userAgent: navigator.userAgent,
language: navigator.language,
screenResolution: `${screen.width}x${screen.height}`,
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone
},
// Add session information
sessionData: {
sessionId: generateSessionId(),
timestamp: new Date().toISOString(),
ipAddress: await getClientIP() // You'd implement this
},
// Add custom risk indicators
riskIndicators: {
customerType: 'returning',
paymentHistory: 'good',
velocityCheck: 'passed'
}
};
console.log('Sending enhanced card transaction');
return enhancedData;
}
onPostAuthorisation
The onPostAuthorisation
callback receives the final transaction result (BaseSubmitResult
).
Success
If the transaction was successful, you'll receive either an AuthorisedSubmitResult
or a CapturedSubmitResult
.
{
state: "Authorised",
providerResponse: {
code: "00",
message: "Approved",
cardVerificationCodeResult: "M",
addressVerificationServiceResult: "Y"
},
fundingData: {
cardVerificationCodeResult: "Matched",
addressVerificationServiceResult: "Y"
}
}
Parameter | Description |
---|---|
| The final state of the transaction.
|
| Details about the provider's response. |
| The raw result code returned by the provider that processed the transaction. |
| The raw message associated with the result code from the provider that processed the transaction. |
| The Card Verification Code (CVC) result returned by the provider. This is a raw data indicating the outcome of the CVC check performed during the transaction processing. |
| The Address Verification Service (AVS) result returned by the provider. This is a raw data indicating the outcome of the AVS check performed during the transaction processing. |
| Details about the payment method. |
| The Card Verification Code (CVC) result in human-readable format. |
| The Address Verification Service (AVS) result in human-readable format. |
Here's an example of what to do with this data:
onPostAuthorisation: (result) => {
console.log('Non-3DS payment result:', result);
if (result.success) {
console.log('Payment successful!');
console.log(`Transaction ID: ${result.transactionId}`);
// Verify no 3DS data (should be null)
if (result.threeDSAuthenticationData) {
console.warn('Unexpected 3DS data in non-3DS transaction');
}
// Check verification results
const fundingData = result.fundingData;
if (fundingData.cardVerificationCodeResult === 'Matched') {
console.log('CVC verification passed');
}
if (fundingData.addressVerificationServiceResult === 'Y') {
console.log('Address verification passed');
}
// Store transaction details
storeTransactionRecord({
transactionId: result.transactionId,
amount: result.amounts.transaction,
currency: result.amounts.currency,
cardType: fundingData.scheme,
processingType: 'non-3ds',
timestamp: new Date().toISOString()
});
// Redirect to success page
window.location.href = `/payment-success?tx=${result.transactionId}`;
} else {
handlePaymentFailure(result);
}
}
Failure (declined)
If the bank or issuer declines the transaction, you'll receive a RefusedSubmitResult
.
{
state: "Refused",
stateData: {
code: "05",
message: "Do not honour"
},
providerResponse: {
code: "05",
message: "Do not honour",
merchantAdvice: {
code: "01",
message: "Try another payment method"
},
cardVerificationCodeResult: "M",
addressVerificationServiceResult: "Y"
},
fundingData: {
cardVerificationCodeResult: "Matched",
addressVerificationServiceResult: "Y"
}
}
Parameter | Description |
---|---|
| The final state of the transaction.
|
| Additional details about the state. |
| The state code. |
| The state message. |
| Details about the provider's response. |
| The raw result code returned by the provider that processed the transaction. |
| The raw message associated with the result code from the provider that processed the transaction. |
| Details about the provider's response. |
| The raw result code returned by the provider that processed the transaction. |
| The raw message associated with the result code from the provider that processed the transaction. |
| The Card Verification Code (CVC) result returned by the provider. This is a raw data indicating the outcome of the CVC check performed during the transaction processing. |
| The Address Verification Service (AVS) result returned by the provider. This is a raw data indicating the outcome of the AVS check performed during the transaction processing. |
| Details about the payment method. |
| The Card Verification Code (CVC) result in human-readable format. |
| The Address Verification Service (AVS) result in human-readable format. |
Here's an example of how to handle failures:
function handlePaymentFailure(result) {
console.error('Non-3DS payment failed:', result);
if (result instanceof RefusedSubmitResult) {
// Bank/issuer declined
const declineCode = result.stateData?.code;
const merchantAdvice = result.providerResponse?.merchantAdvice;
switch (declineCode) {
case '05': // Do not honor
showError('Payment declined by your bank. Please try a different card.');
break;
case '14': // Invalid card
showError('Invalid card details. Please check and try again.');
break;
case '51': // Insufficient funds
showError('Insufficient funds. Please try a different payment method.');
break;
case '54': // Expired card
showError('Your card has expired. Please use a different card.');
break;
default:
showError(merchantAdvice?.message || 'Payment declined. Please try again.');
}
// Log decline for analysis
logPaymentDecline({
transactionId: result.transactionId,
declineCode: declineCode,
declineReason: result.stateData?.message,
merchantAdvice: merchantAdvice?.message
});
} else {
// System error
showError('Payment failed due to a system error. Please try again.');
logSystemError('Non-3DS payment system failure', result);
}
// Re-enable payment form
enablePaymentForm();
hideLoadingSpinner();
}
onSubmitError
If an error occurs during the payment processing, you'll receive error details.
{
errorCode: "VALIDATION_FAILED",
errorReason: "Card number validation failed",
correlationId: "corr_12345678",
httpStatusCode: 400,
details: ["Invalid card number format"]
}
Here's an example of how to handle this data:
onSubmitError: (error) => {
console.error('Non-3DS payment error:', error);
// Handle specific error types
switch (error.code) {
// Validation Errors
case 'VALIDATION_FAILED':
showError('Please check your payment details and try again.');
highlightInvalidFields();
break;
case 'INVALID_CARD_NUMBER':
showError('Invalid card number. Please check and try again.');
focusField('cardNumber');
break;
case 'INVALID_EXPIRY_DATE':
showError('Invalid expiry date. Please check and try again.');
focusField('expiryDate');
break;
case 'INVALID_CVC':
showError('Invalid security code. Please check and try again.');
focusField('cvc');
break;
// Processing Errors
case 'TOKENIZATION_FAILED':
showError('Unable to process card details. Please try again.');
logError('Tokenization failed for non-3DS payment', error);
break;
case 'NETWORK_ERROR':
showError('Connection failed. Please check your internet and try again.');
break;
case 'GATEWAY_TIMEOUT':
showError('Payment system is busy. Please try again in a moment.');
break;
case 'SERVICE_UNAVAILABLE':
showError('Payment service temporarily unavailable. Please try again later.');
break;
// Configuration Errors
case 'MERCHANT_NOT_CONFIGURED':
showError('Payment setup error. Please contact support.');
logError('Merchant configuration error', error);
break;
default:
showError('An unexpected error occurred. Please try again.');
logError('Unknown non-3DS payment error', error);
}
// Re-enable the payment form
hideLoadingSpinner();
enablePaymentForm();
// Log error for monitoring
logPaymentError({
errorCode: error.code,
errorReason: error.message,
correlationId: error.correlationId,
timestamp: new Date().toISOString(),
paymentType: 'non-3ds'
});
}
Updated 3 days ago