Before deploying your payment connector to production, you must thoroughly test all payment flows to ensure your connector works correctly with the VTEX Payment Gateway.
Test suite requirements
Your connector must be able to handle the VTEX test suite, which validates that your implementation follows the Payment Provider Protocol correctly.
The isTestSuite property is automatically available in your PaymentProvider class and indicates when requests are coming from the VTEX test suite.
Implementing test suite support
Your connector should check the isTestSuite flag and implement special logic for test requests:
export default class TestSuiteApprover extends PaymentProvider {
public async authorize(
authorization: AuthorizationRequest
): Promise<AuthorizationResponse> {
if (this.isTestSuite) {
// Return consistent responses for test suite
return executeAuthorization(authorization, response =>
this.saveAndRetry(authorization, response)
)
}
// Your production implementation
throw new Error('Not implemented')
}
}
Persisting test responses
The test suite may retry the same payment ID multiple times. Your connector must return consistent responses for the same payment ID:
const authorizationsBucket = 'authorizations'
const persistAuthorizationResponse = async (
vbase: VBase,
resp: AuthorizationResponse
) => vbase.saveJSON(authorizationsBucket, resp.paymentId, resp)
const getPersistedAuthorizationResponse = async (
vbase: VBase,
req: AuthorizationRequest
) =>
vbase.getJSON<AuthorizationResponse | undefined>(
authorizationsBucket,
req.paymentId,
true
)
You need to add the vbase-read-write policy to your manifest.json to persist responses.
Test card numbers
The example connector provides test card numbers that trigger different payment flows. Use these cards to test various scenarios:
Card authorization flows
| Card number | Flow | Behavior |
|---|
4444333322221111 | Authorize | Immediately approved |
4444333322221112 | Denied | Immediately denied |
4222222222222224 | AsyncApproved | Pending, then approved via callback |
4222222222222225 | AsyncDenied | Pending, then denied via callback |
| Tokenized card (null) | Redirect | Redirect to payment page |
Implementation example
The flow logic is defined in node/flow.ts:
const cardResponses: Record<CardNumber, Flow> = {
'4444333322221111': 'Authorize',
'4444333322221112': 'Denied',
'4222222222222224': 'AsyncApproved',
'4222222222222225': 'AsyncDenied',
null: 'Redirect',
}
Testing payment flows
Test immediate approval
Use card 4444333322221111 to test successful authorization:flows.Authorize = request =>
Authorizations.approve(request, {
authorizationId: randomString(),
nsu: randomString(),
tid: randomString(),
})
Test immediate denial
Use card 4444333322221112 to test declined payments:flows.Denied = request =>
Authorizations.deny(request, { tid: randomString() })
Test async approval
Use card 4222222222222224 to test the retry flow:flows.AsyncApproved = (request, retry) => {
retry(
Authorizations.approve(request, {
authorizationId: randomString(),
nsu: randomString(),
tid: randomString(),
})
)
return Authorizations.pending(request, {
delayToCancel: 1000,
tid: randomString(),
})
}
The retry callback asks the Payment Gateway to call the create payment route again. Your connector must return a consistent approved/denied response on subsequent calls.
Test bank invoice
Test bank invoice (boleto) payments:flows.BankInvoice = (request, retry) => {
retry(
Authorizations.approve(request, {
authorizationId: randomString(),
nsu: randomString(),
tid: randomString(),
})
)
return Authorizations.pendingBankInvoice(request, {
delayToCancel: 1000,
paymentUrl: randomUrl(),
tid: randomString(),
})
}
Test redirect flow
Test redirect-based payment methods:flows.Redirect = (request, retry) => {
retry(
Authorizations.approve(request, {
authorizationId: randomString(),
nsu: randomString(),
tid: randomString(),
})
)
return Authorizations.redirect(request, {
delayToCancel: 1000,
redirectUrl: randomUrl(),
tid: randomString(),
})
}
Test cancellation
Verify that cancellation requests are handled correctly:public async cancel(
cancellation: CancellationRequest
): Promise<CancellationResponse> {
if (this.isTestSuite) {
return Cancellations.approve(cancellation, {
cancellationId: randomString(),
})
}
throw new Error('Not implemented')
}
Test settlement and refund
Implement settlement and refund flows:public async settle(
settlement: SettlementRequest
): Promise<SettlementResponse> {
if (this.isTestSuite) {
return Settlements.deny(settlement)
}
throw new Error('Not implemented')
}
public async refund(refund: RefundRequest): Promise<RefundResponse> {
if (this.isTestSuite) {
return Refunds.deny(refund)
}
throw new Error('Not implemented')
}
Testing with IO Connectors
To test your connector in a real VTEX store environment:
Your account MUST BE ALLOWED to use IO Connectors. Submit a request in the #provider-review Slack channel using the “Allow Account to test” workflow.
Launch a beta version
Publish a beta version of your connector:This creates a version like [email protected]. Install on master workspace
Install the beta version on your master workspace:Wait approximately 1 hour for the installation to complete. Configure the connector
Go to the connector configuration page:https://${account}.myvtex.com/admin/pci-gateway/#/affiliations/vtex-payment-provider-test-v0/
The URL format is: ${vendor}-${appName}-${appMajor} Enable test mode
- Change the toggle configuration to Test
- Click Save and refresh the page
- Re-enter the configuration
- Set the
workspace field (you can use master or your development workspace)
Configure payment condition
Configure a payment condition with your newly created connector and wait 10 minutes for it to appear on checkout.
Place test orders
Make sure you have products for sale in your store, then place test orders using the test card numbers to verify all payment flows work correctly.
Retry flow vs callback flow
Callback flow is replaced by retry flow for IO Connectors. Payment Providers implemented using VTEX IO cannot callback the Payment Gateway directly with payment status updates.
Instead of callbacks, use the retry mechanism:
// Request a retry from Payment Gateway
this.retry(request)
The retry flow allows your connector to ask the Payment Gateway to call the create payment route again. Your connector must respond with approved/denied consistently.
Next steps
Once your connector passes all tests: