The Authorize flow uses a quote to run card authorizations with 3DS. The widget creates the transaction, handles required bank redirects, and polls until a terminal status.
Prerequisites
A quote ID for the intended transaction (built from the user’s selected origin/destination).
The user is onboarded and verified (KYC is complete).
The necessary capability for the transaction is enabled for the user (e.g.: card deposit or card withdrawal)
The external account (card) is valid and allowed for authorization.
Flow overview
The authorization_event callback in the diagram maps to the Widget complete event and returns { transaction, trigger }.
What the flow covers
Create an Authorize session using an existing quote.
Run 3DS/bank authentication via the Widget and create the transaction.
Receive terminal status or keep monitoring if retries are exhausted.
Integration
Here’s how to run an authorization with the Payment Widget SDK.
Create Authorize session
You need a quote first (often produced after Select for Deposit/Withdrawal). Then create the payment session for flow: "authorize".
Create a quote
curl -X POST https://api.enterprise.uphold.com/transactions/quote \
-H "Authorization: Bearer <API_TOKEN>" \
-H "Content-Type: application/json" \
-H "X-On-Behalf-Of: user <USER_ID>" \
-d '{
"denomination": {
"amount": "50.00",
"asset": "GBP",
"target": "origin"
},
"destination": {
"type": "account",
"id": "79f52400-5c36-4ea0-94a8-00f406e0d905"
},
"origin": {
"type": "external-account",
"id": "7d5928c5-8ac4-4b0d-8b45-f332ba6a9de7"
}
}'
const createQuote = async ( transactionData ) => {
try {
const quoteResponse = await fetch ( 'https://api.enterprise.uphold.com/transactions/quote' , {
method: 'POST' ,
headers: {
'Authorization' : 'Bearer <API_TOKEN>' ,
'Content-Type' : 'application/json' ,
'X-On-Behalf-Of' : 'user <USER_ID>'
},
body: JSON . stringify ( transactionData )
});
if ( ! quoteResponse . ok ) {
throw new Error ( 'Failed to create quote' );
}
const quote = await quoteResponse . json ();
return quote ;
} catch ( error ) {
console . error ( 'Failed to create quote:' , error );
throw error ;
}
};
Create Authorize session
curl -X POST https://api.enterprise.uphold.com/widgets/payment/sessions \
-H "Authorization: Bearer <API_TOKEN>" \
-H "Content-Type: application/json" \
-H "X-On-Behalf-Of: user <USER_ID>" \
-d '{
"flow": "authorize",
"data": {
"quoteId": "<QUOTE_ID>"
}
}'
const createAuthorizeSession = async ( transactionData ) => {
// First, create a quote
const quote = await createQuote ( transactionData );
// Then, create the authorize session using the quote id
const sessionResponse = await fetch ( 'https://api.enterprise.uphold.com/widgets/payment/sessions' , {
method: 'POST' ,
headers: {
'Authorization' : 'Bearer <API_TOKEN>' ,
'Content-Type' : 'application/json' ,
'X-On-Behalf-Of' : 'user <USER_ID>'
},
body: JSON . stringify ({
flow: 'authorize' ,
data: { quoteId: quote . id }
})
});
if ( ! sessionResponse . ok ) {
throw new Error ( 'Failed to create authorization session' );
}
return await sessionResponse . json ();
};
import { PaymentWidget } from '@uphold/enterprise-payment-widget-web-sdk' ;
const initializeAuthorizeWidget = async ( transactionData ) => {
const session = await createAuthorizeSession ( transactionData );
const widget = new PaymentWidget < 'authorize' >( session . session , { debug: true });
setupEventHandlers ( widget );
widget . mountIframe ( document . getElementById ( 'payment-container' ));
};
Handle the complete event
The complete event does not guarantee success. Always check transaction.status and trigger.reason.
widget . on ( 'complete' , ( event ) => {
const { transaction , trigger } = event . detail . value ;
handleAuthorizeComplete ( transaction , trigger );
widget . unmount ();
});
Understand trigger reasons
const handleAuthorizeComplete = ( transaction , trigger ) => {
if ( trigger . reason === 'transaction-status-changed' ) {
handleTransactionStatusChange ( transaction );
} else if ( trigger . reason === 'max-retries-reached' ) {
handleProcessingTransaction ( transaction );
}
};
const handleTransactionStatusChange = ( transaction ) => {
switch ( transaction . status ) {
case 'completed' :
handleTransactionSuccess ( transaction );
break ;
case 'failed' :
handleTransactionFailure ( transaction );
break ;
default :
break ;
}
};
const handleTransactionFailure = ( transaction ) => {
switch ( transaction . statusDetails . reason ) {
case 'card-declined-by-bank' :
case 'card-expired' :
case 'card-permanently-declined-by-bank' :
case 'card-unauthorized' :
case 'card-unsupported' :
case 'insufficient-funds' :
case 'provider-maximum-limit-exceeded' :
case 'velocity' :
case 'unspecified-error' :
default :
// Map to user-facing error messages
break ;
}
};
const handleProcessingTransaction = ( transaction ) => {
// Widget stopped polling; continue monitoring via webhooks or polling Get Transaction
showProcessingMessage ( 'Your transaction is being processed. Please check again later.' );
};
Handle the cancel event
widget . on ( 'cancel' , () => {
handleAuthorizeCancelled ();
widget . unmount ();
});
const handleAuthorizeCancelled = () => {
// Return user to your payment screen
};
Handle the error event
widget . on ( 'error' , ( event ) => {
handleAuthorizeError ( event . detail . error );
widget . unmount ();
});
const handleAuthorizeError = ( error ) => {
const { code , details } = error ;
switch ( code ) {
case 'entity_not_found' :
// Quote expired
break ;
case 'insufficient_balance' :
break ;
case 'operation_not_allowed' :
// e.g., duplicate-withdrawal, card-unauthorized, unspecified-error
break ;
case 'user_capability_failure' :
break ;
default :
break ;
}
};
Complete implementation example
import { PaymentWidget } from '@uphold/enterprise-payment-widget-web-sdk' ;
class TransactionAuthorizer {
constructor () {
this . widget = null ;
}
async authorize ( transactionData ) {
try {
const session = await this . createAuthorizeSession ( transactionData );
this . widget = new PaymentWidget < 'authorize' >( session . session , { debug: true });
this . setupEventHandlers ();
this . widget . mountIframe ( document . getElementById ( 'payment-container' ));
} catch ( error ) {
this . handleInitializationError ( error );
}
}
async createAuthorizeSession ( transactionData ) {
const quote = await createQuote ( transactionData );
const response = await fetch ( 'https://api.enterprise.uphold.com/widgets/payment/sessions' , {
method: 'POST' ,
headers: {
'Authorization' : 'Bearer <API_TOKEN>' ,
'Content-Type' : 'application/json' ,
'X-On-Behalf-Of' : 'user <USER_ID>'
},
body: JSON . stringify ({
flow: 'authorize' ,
data: { quoteId: quote . id }
})
});
if ( ! response . ok ) {
throw new Error ( 'Failed to create authorization session' );
}
return await response . json ();
}
setupEventHandlers () {
this . widget . on ( 'complete' , ( event ) => {
const { transaction , trigger } = event . detail . value ;
this . handleAuthorizeComplete ( transaction , trigger );
this . widget . unmount ();
});
this . widget . on ( 'cancel' , () => {
this . handleCancellation ();
this . widget . unmount ();
});
this . widget . on ( 'error' , ( event ) => {
this . handleError ( event . detail . error );
this . widget . unmount ();
});
}
handleAuthorizeComplete ( transaction , trigger ) {
if ( trigger . reason === 'transaction-status-changed' ) {
this . handleTransactionStatusChange ( transaction );
} else if ( trigger . reason === 'max-retries-reached' ) {
this . handleProcessingTransaction ( transaction );
}
}
handleTransactionStatusChange ( transaction ) {
if ( transaction . status === 'completed' ) {
// Success path
return ;
}
if ( transaction . status === 'failed' ) {
this . handleTransactionFailure ( transaction );
}
}
handleTransactionFailure ( transaction ) {
// Map statusDetails.reason to user-friendly messaging
}
handleProcessingTransaction ( transaction ) {
// Keep monitoring via webhooks or polling Get Transaction
}
handleCancellation () {
// User exited the flow
}
handleError ( error ) {
// Show user-friendly error
}
handleInitializationError ( error ) {
// Show init error
}
}
// Example transaction data
const transactionData = {
denomination: {
amount: '100.00' ,
asset: 'USD' ,
target: 'origin'
},
destination: {
type: 'account' ,
id: '79f52400-5c36-4ea0-94a8-00f406e0d905'
},
origin: {
type: 'external-account' ,
id: '7d5928c5-8ac4-4b0d-8b45-f332ba6a9de7'
}
};
const authorizer = new TransactionAuthorizer ();
authorizer . authorize ( transactionData );
See all 118 lines
Additional resources