SDK Integration (V2)
Quick Reference
The A55Pay JavaScript SDK V2 runs in the buyer's browser: it collects card data, runs Device Data Collection (DDC), handles 3DS authentication, processes the payment, and surfaces callbacks. Raw card data does not pass through your origin servers.
Why SDK
| Benefit | Detail |
|---|---|
| Secure by default | Card data stays in browser — A55 handles encryption |
| Fastest integration | One script tag + one function call |
| Built-in DDC & 3DS | SDK runs device fingerprinting and authentication automatically |
| Event callbacks | onSuccess, onError, onReady / onClose for full control |
How it works
Step-by-step integration
Load the SDK
Add the script tag to your checkout page:
<script src="https://cdn.jsdelivr.net/npm/a55pay-sdk"></script>
Pin a minor version in production (e.g., a55pay-sdk@1.2.x) for reproducible behavior across deploys.
Create a charge on your backend
Your backend creates a charge without card data — the SDK will collect and submit card data from the browser.
- cURL
- Python
- Node.js
- Go
- Java
- PHP
curl -sS -X POST "https://core-manager.a55.tech/api/v1/bank/wallet/charge/" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"wallet_uuid": "00000000-0000-4000-8000-000000000001",
"merchant_id": "merchant_123",
"payer_name": "Jane Doe",
"payer_email": "jane@example.com",
"payer_tax_id": "12345678901",
"currency": "BRL",
"installment_value": 10000,
"installment_count": 1,
"type_charge": "credit_card",
"description": "SDK charge",
"reference_external_id": "sdk_001",
"webhook_url": "https://merchant.example/webhooks/a55",
"redirect_url": "https://merchant.example/return"
}'
import os, requests
r = requests.post(
"https://core-manager.a55.tech/api/v1/bank/wallet/charge/",
headers={"Authorization": f"Bearer {os.environ['ACCESS_TOKEN']}",
"Content-Type": "application/json"},
json={
"wallet_uuid": "00000000-0000-4000-8000-000000000001",
"merchant_id": "merchant_123",
"payer_name": "Jane Doe", "payer_email": "jane@example.com",
"payer_tax_id": "12345678901",
"currency": "BRL", "installment_value": 10000, "installment_count": 1,
"type_charge": "credit_card",
"reference_external_id": "sdk_001",
"webhook_url": "https://merchant.example/webhooks/a55",
},
)
charge_uuid = r.json()["uuid"]
const res = await fetch(
"https://core-manager.a55.tech/api/v1/bank/wallet/charge/",
{
method: "POST",
headers: {
Authorization: `Bearer ${process.env.ACCESS_TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
wallet_uuid: "00000000-0000-4000-8000-000000000001",
merchant_id: "merchant_123",
payer_name: "Jane Doe", payer_email: "jane@example.com",
payer_tax_id: "12345678901",
currency: "BRL", installment_value: 10000, installment_count: 1,
type_charge: "credit_card",
reference_external_id: "sdk_001",
webhook_url: "https://merchant.example/webhooks/a55",
}),
}
);
const { uuid: chargeUuid } = await res.json();
payload := map[string]interface{}{
"wallet_uuid": "00000000-0000-4000-8000-000000000001",
"type_charge": "credit_card", "currency": "BRL",
"installment_value": 10000, "installment_count": 1,
"reference_external_id": "sdk_001",
"webhook_url": "https://merchant.example/webhooks/a55",
}
body, _ := json.Marshal(payload)
req, _ := http.NewRequest("POST",
"https://core-manager.a55.tech/api/v1/bank/wallet/charge/", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer "+os.Getenv("ACCESS_TOKEN"))
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
var result struct{ UUID string `json:"uuid"` }
json.NewDecoder(resp.Body).Decode(&result)
String json = """
{"wallet_uuid":"00000000-0000-4000-8000-000000000001",
"type_charge":"credit_card","currency":"BRL",
"installment_value":10000,"installment_count":1,
"reference_external_id":"sdk_001",
"webhook_url":"https://merchant.example/webhooks/a55"}
""";
HttpRequest req = HttpRequest.newBuilder()
.uri(URI.create("https://core-manager.a55.tech/api/v1/bank/wallet/charge/"))
.header("Authorization", "Bearer " + System.getenv("ACCESS_TOKEN"))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(json)).build();
HttpResponse<String> resp = HttpClient.newHttpClient()
.send(req, HttpResponse.BodyHandlers.ofString());
String chargeUuid = new JSONObject(resp.body()).getString("uuid");
$payload = [
"wallet_uuid" => "00000000-0000-4000-8000-000000000001",
"type_charge" => "credit_card", "currency" => "BRL",
"installment_value" => 10000, "installment_count" => 1,
"reference_external_id" => "sdk_001",
"webhook_url" => "https://merchant.example/webhooks/a55",
];
$ch = curl_init("https://core-manager.a55.tech/api/v1/bank/wallet/charge/");
curl_setopt_array($ch, [
CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("ACCESS_TOKEN"),
"Content-Type: application/json"],
CURLOPT_POSTFIELDS => json_encode($payload),
]);
$chargeUuid = json_decode(curl_exec($ch))->uuid;
Call A55Pay.payV2() from the browser
Pass the chargeUuid from your backend and the card data collected in the browser:
A55Pay.payV2({
charge_uuid: chargeUuid,
userData: {
name: 'Jane Doe',
email: 'jane@example.com',
document: '12345678901',
card_number: '4111111111111111',
card_expiry_month: '12',
card_expiry_year: '2030',
card_cvv: '123',
address: {
street: 'Av. Paulista 1000',
city: 'São Paulo',
state: 'SP',
zip_code: '01310100',
country: 'BR',
},
},
onSuccess: (payload) => {
// Payment confirmed — show success to buyer
console.log('Charge confirmed:', payload);
},
onError: (err) => {
// Decline or error — show appropriate message
console.error('Charge failed:', err);
},
onReady: () => {
// SDK initialized — you can show the payment form
},
});
The SDK will automatically: run DDC -> submit payment -> handle 3DS challenge (if needed) -> call onSuccess or onError.
Handle callbacks
| Callback | When it fires | What to do |
|---|---|---|
onSuccess(payload) | Payment completed (confirmed or paid) | Show success, redirect to order page |
onError(err) | Decline, error, or authentication failure | Show user-friendly error message |
onReady() | SDK initialized and ready | Optionally show the card form |
Expected onSuccess payload:
{
"chargeUuid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"status": "confirmed",
"reference_external_id": "sdk_001"
}
Expected onError payload:
{
"chargeUuid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"status": "error",
"message": "Card declined by issuer"
}
Confirm via webhook
Always use the webhook as the source of truth. The buyer might close the browser before onSuccess fires.
{
"charge_uuid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"status": "paid",
"transaction_reference": "txn_ref_001"
}
A55Pay.open(config) — iframe checkout
For a fully hosted checkout experience inside an iframe or modal:
const { close } = A55Pay.open({
checkoutUuid: chargeUuid,
display: 'modal', // 'modal' or 'inline'
containerId: 'checkout', // required for 'inline'
onSuccess: (payload) => {
console.log('Payment confirmed:', payload);
},
onError: (err) => {
console.error('Payment error:', err);
},
onClose: () => {
console.log('Checkout closed by user');
},
onEvent: (event) => {
console.log('Checkout event:', event);
},
});
| Callback | payV2 | open |
|---|---|---|
onSuccess | Yes | Yes |
onError | Yes | Yes |
onReady | Yes | No |
onClose | No | Yes |
onEvent | No | Yes |
Test with sandbox cards
| Card Number | Brand | Scenario | Expected Status |
|---|---|---|---|
4111 1111 1111 1111 | Visa | Successful payment | confirmed |
5500 0000 0000 0004 | Mastercard | Successful payment | confirmed |
4000 0000 0000 0002 | Visa | Card declined | declined |
4000 0000 0000 0101 | Visa | 3DS challenge required | confirmed |
4000 0000 0000 0069 | Visa | Processing error | error |
Never log PAN, CVV, or cryptograms from callback payloads. The SDK handles card data in the browser context — keep it there.
Want higher approval rates?
Visa Data Only shares enriched data with issuers through 3DS rails — without any challenge. Square's pilot showed up to +646 basis points improvement.
If you use payV2, the SDK already collects device data via DDC. You can combine this with data_only: true on the backend charge to maximize approvals without adding friction.