The Authorize use case allows users to authorize card transactions in a secure manner.
The Payment Widget handles the entire transaction process: it creates the transaction, redirects users to their bank’s confirmation page when 3D Secure authentication is required, and continuously monitors the transaction status until completion.Support for Apple Pay and Google Pay transactions is coming soon.
Before initializing the Widget, you must first create a quote for a transaction (e.g. with the help of Select for Deposit or Select for Withdrawal), then create a payment session for the Authorize use case.
First, create a transaction quote using the Create Quote endpoint.
The following example will create a quote for a deposit transaction using an external account obtained from the result of the Select for Deposit use case:
The example code below is for web applications. For native applications using a WebView, you’ll still need to implement communication through a bridge to send events to the native side, as highlighted in the Using the SDK page.
Once you have the session, initialize the Payment Widget for the Authorize flow:
Copy
Ask AI
import { PaymentWidget } from '@uphold/enterprise-payment-widget-web-sdk';const initializeAuthorizeWidget = async (transactionData) => { // Create the quote and session const session = await createAuthorizeSession(transactionData); // Initialize the Widget with the session const widget = new PaymentWidget<'authorize'>(session.session, { debug: true }); // Set up event handlers (see sections below) setupEventHandlers(widget); // Mount the Widget widget.mountIframe(document.getElementById('payment-container'));};
When the complete event is raised, you will receive the transaction alongside a trigger property containing the reason why the event was raised.
The complete event being raised does NOT guarantee that the transaction was completed successfully. You MUST check the transaction status and trigger reason to determine the actual outcome and handle the event correctly.
Copy
Ask AI
widget.on('complete', (event) => { const result = event.detail.value; console.log('Authorize flow completed:', result); // result contains transaction and trigger information const { transaction, trigger } = result; // ALWAYS check the trigger reason and transaction status handleAuthorizeComplete(transaction, trigger); // Clean up the Widget widget.unmount();});
The trigger.reason indicates why the complete event was fired:
Copy
Ask AI
const handleAuthorizeComplete = (transaction, trigger) => { console.log('Trigger reason:', trigger.reason); console.log('Transaction status:', transaction.status); if (trigger.reason === 'transaction-status-changed') { // Transaction status has changed - check if it succeeded or failed handleTransactionStatusChange(transaction); } else if (trigger.reason === 'max-retries-reached') { // Widget stopped polling - transaction may still be processing handleProcessingTransaction(transaction); }};
When the trigger reason is transaction-status-changed, examine the transaction status:
Copy
Ask AI
const handleTransactionStatusChange = (transaction) => { switch (transaction.status) { case 'completed': // ✅ Transaction was successful handleTransactionSuccess(transaction); break; case 'failed': // ❌ Transaction failed handleTransactionFailure(transaction); break; default: break; }};const handleTransactionSuccess = (transaction) => { console.log('✅ Transaction completed successfully:', transaction.id); // Update UI, redirect to success page, etc. navigateToSuccessPage(transaction);};const handleTransactionFailure = (transaction) => { console.error('❌ Transaction failed:', transaction.id); // Present a specific error message (See next section)};
When the transaction status is failed, you can obtain more information in the transaction’s statusDetails field. This will help you return a more specific message to the user:
Copy
Ask AI
const handleTransactionFailure = (transaction) => { switch(transaction.statusDetails.reason) { // Transaction amount outside boundaries case 'amount-limit-exceed': break; // Card declined by bank case 'card-declined-by-bank': break; // Card has expired case 'card-expired': break; // Card is permanently declined by bank case 'card-permanently-declined-by-bank': break; // Card was not authorized case 'card-unauthorized': break; // Card is not supported case 'card-unsupported': break; // The specified account does not have sufficient funds to carry the transaction case 'insufficient-funds': break; // Too many transactions in a short period of time case 'velocity': break; // Error not specified case 'unspecified-error': break; // Handle unmatched reason (when new reasons are added) default: break; }};
When the trigger reason is max-retries-reached, the Widget has stopped polling but the transaction may still be processing. This occurs when the transaction requires more time than usual to complete and will reach a final state at a later time:
Copy
Ask AI
const handleProcessingTransaction = (transaction) => { console.log('⏰ Polling timeout reached for transaction:', transaction.id); // Transaction is still processing - continue monitoring showProcessingMessage('Your transaction is being processed. Check again later.');};
The cancel event is fired when the user cancels the authorization process.
Copy
Ask AI
widget.on('cancel', () => { console.log('User cancelled transaction authorization'); // Handle cancellation handleAuthorizeCancelled(); // Clean up the Widget widget.unmount();});const handleAuthorizeCancelled = () => { // Show a message to the user showMessage('Transaction authorization was cancelled'); // Redirect back to the transaction setup page navigateBack();};
Another unrecoverable situation like no networking connection or server is down
You can use the error’s code and details properties to distinguish between those kinds of errors.
Copy
Ask AI
widget.on('error', (event) => { const error = event.detail.error; console.error('Authorization Widget error:', error); // Handle the error handleAuthorizeError(error); // Clean up the Widget widget.unmount();});const handleAuthorizeError = (error) => { const { code, details } = error; switch(code) { // Quote has expired case 'entity_not_found': break; // No sufficient funds in the account case 'insufficient_balance': break; case 'operation_not_allowed': // Duplicate withdrawal in 2 minutes if(details.reasons.includes('duplicate-withdrawal')) { } // Card was unauthorized by bank else if(details.reasons.includes('card-unauthorized')) { } // An unknown error happened else if(details.reasons.includes('unspecified-error')) { } break; // User capability does not allow the operation case 'user_capability_failure': break; // Handle other reasons like no network connection or server is down default: break; }};