Migrate from Triple-A

Move from Triple-A to CoinCircuit: swap OAuth2 for an API key, update webhook signing, and map every field.

Open the interactive version or read this guide as markdown.

Triple-A and CoinCircuit are both licensed crypto payment platforms — you create a payment, the customer sends crypto to an address, and your server receives a notification when it settles. The integration model is similar, but the two platforms differ significantly in authentication and webhook signing. Triple-A uses OAuth2 client credentials for every API call; CoinCircuit uses a single static API key. Triple-A signs notifications with a per-payment secret; CoinCircuit signs with a global webhook secret using HMAC-SHA256 on the raw request body.

Concept mapping

Triple-A CoinCircuit Notes
OAuth2 Bearer token x-api-key header One static key replaces the token flow entirely.
client_id + client_secret API key Generate once in the dashboard under Developer > API Keys.
merchant_key CoinCircuit keys are already scoped to your account.
Payment (type: triplea) Payment (checkout session) Redirect the customer to checkoutUrl.
Invoice (type: invoice) Invoice Itemised billing with a hosted pay link.
Widget (type: widget) Checkout session CoinCircuit's hosted checkout embeds the same way.
notify_url webhookUrl Per-payment webhook URL.
notify_secret Webhook secret Set once in the dashboard; CoinCircuit signs all events with it.
payment_reference reference Unique ID returned when you create the payment.
order_id metadata.orderId Store your internal ID in metadata — it comes back on every event.
webhook_data metadata Any extra key-value data passed through to webhooks.
Payment tier (good / hold / invalid) Webhook events See event mapping in Step 4.
/api/v2/payout/withdraw/... POST /api/v1/payouts Save a recipient once, then pay out to it.
sandbox: true in request body Sandbox base URL Use https://sandbox-api.coincircuit.io with test keys.

Step 1 — Replace OAuth2 with an API key

Triple-A requires a token exchange before every session. You POST your client_id and client_secret to get a Bearer token, then send it on every subsequent request.

Triple-A

POST https://api.triple-a.io/api/v2/oauth/token
Content-Type: application/x-www-form-urlencoded

client_id=oacid-*****&client_secret=Qi42***&grant_type=client_credentials
{ "access_token": "1ba8***", "token_type": "bearer", "expires_in": 315359999 }

Every subsequent request then sends Authorization: Bearer 1ba8***.

CoinCircuit

Delete the token exchange entirely. Every request sends one static header:

x-api-key: sk_live_your_key

Generate your key in the dashboard under Developer > API Keys. Test keys work against https://sandbox-api.coincircuit.io; live keys work against https://api.coincircuit.io.


Step 2 — Create payments

Triple-A has three payment types in one endpoint: triplea (hosted redirect), widget (embedded), and invoice (invoice link). The merchant_key selects which merchant account receives funds, and notify_secret signs each notification individually.

Triple-A

POST https://api.triple-a.io/api/v2/payment
Authorization: Bearer <token>
Content-Type: application/json

{
  "type": "triplea",
  "merchant_key": "mkey-ckfhqahxy04g6e4qs6t3f00nl",
  "order_currency": "USD",
  "order_amount": 10,
  "order_id": "TSGA267KL",
  "notify_url": "https://your-site.com/webhook",
  "notify_secret": "Cf9mx4nAvRuy5vwBY2FCtaKr",
  "payer_email": "alice@example.com",
  "success_url": "https://your-site.com/success",
  "cancel_url": "https://your-site.com/cancel",
  "webhook_data": { "order_id": "TSGA267KL" }
}

The response includes hosted_url — redirect the customer there.

CoinCircuit

POST https://api.coincircuit.io/api/v1/payments
x-api-key: sk_live_your_key
Content-Type: application/json

{
  "title": "Order TSGA267KL",
  "description": "Your order description",
  "amount": "10.00",
  "currency": "USD",
  "customer": { "email": "alice@example.com" },
  "webhookUrl": "https://your-site.com/webhook",
  "successUrl": "https://your-site.com/success",
  "cancelUrl": "https://your-site.com/cancel",
  "metadata": { "orderId": "TSGA267KL" }
}

The response includes checkoutUrl — redirect the customer there. CoinCircuit handles exchange rate locking and crypto selection internally.

Field mapping

Triple-A field CoinCircuit field Notes
type Not needed; CoinCircuit has one payment type.
merchant_key Your API key already scopes to your account.
order_currency currency NGN or USD.
order_amount amount Pass as a string, e.g. "10.00".
order_id metadata.orderId Stored in metadata; returned on every webhook.
notify_url webhookUrl Per-payment webhook URL.
notify_secret Webhook secret Set once in the dashboard; no longer per-payment.
payer_email customer.email Required.
payer_name customer.firstName + customer.lastName Split into two fields.
payer_phone customer.phone Optional.
success_url successUrl Redirect on successful payment.
cancel_url cancelUrl Redirect on cancellation.
webhook_data metadata Any key-value data echoed back in webhooks.

Step 3 — Replace invoices

Triple-A's invoice type (type: invoice) emails the customer a hosted invoice URL. CoinCircuit invoices are line-item based.

Triple-A

{
  "type": "invoice",
  "merchant_key": "mkey-ckfhqahxy04g6e4qs6t3f00nl",
  "order_currency": "USD",
  "order_amount": 100,
  "order_id": "INV-001",
  "invoice_desc": "Consulting services — June",
  "notify_payer": true,
  "payer_email": "client@example.com",
  "payer_name": "Alice Tan"
}

CoinCircuit

Map invoice_desc to description and break the total into items. The reference field replaces order_id.

POST https://api.coincircuit.io/api/v1/invoices
x-api-key: sk_live_your_key
Content-Type: application/json

{
  "description": "Consulting services — June",
  "currency": "USD",
  "expiresAt": "2026-08-01T00:00:00Z",
  "reference": "INV-001",
  "customer": { "email": "client@example.com", "firstName": "Alice" },
  "items": [
    { "name": "Consulting services — June", "quantity": 1, "unitPrice": 100 }
  ]
}

The response includes invoiceUrl — share it with your customer or embed it in your own email.


Step 4 — Migrate webhooks

Triple-A POSTs to your notify_url when a payment changes tier. Each payment carries its own notify_secret; if omitted, Triple-A auto-generates one and returns it in the create-payment response.

Triple-A notification payload

{
  "event": "payment",
  "merchant_key": "mkey-ckfhqahxy04g6e4qs6t3f00nl",
  "payment_reference": "SDF-453672-PMT",
  "status": "good",
  "payment_tier": "good",
  "order_currency": "USD",
  "payment_amount": 10,
  "webhook_data": { "order_id": "TSGA267KL" }
}

CoinCircuit webhook handler

CoinCircuit uses a single global webhook secret and signs every event with HMAC-SHA256 on the raw request body, sending the result in the x-coincircuit-signature header.

const crypto = require('crypto');

app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
  const sig = req.headers['x-coincircuit-signature'];
  const expected = crypto
    .createHmac('sha256', process.env.WEBHOOK_SECRET)
    .update(req.body)
    .digest('hex');

  if (!crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(sig))) {
    return res.status(401).send('Invalid signature');
  }

  const event = JSON.parse(req.body);
  res.sendStatus(200);
});

Use express.raw() so you verify the exact bytes CoinCircuit signed, before any JSON parsing.

Key signing differences

Triple-A CoinCircuit
Per-payment notify_secret Single global webhook secret in the dashboard
No documented signature header x-coincircuit-signature (HMAC-SHA256 hex)
notify_url per payment webhookUrl per payment, or global URL in dashboard
webhook_data echoed in body metadata echoed in every event

Payment tier mapping

Triple-A status / payment_tier CoinCircuit event Meaning
none No funds received yet; CoinCircuit omits pre-deposit events.
short transaction.received Funds detected on-chain, not yet confirmed.
hold transaction.confirmed Transaction confirmed on the blockchain.
good payment.completed Payment settled in full — fulfil the order on this event.
invalid payment.expired Payment expired or could not be completed.
payment.partial Partial deposit received.

Fulfil orders on payment.completed (was good in Triple-A). Your order_id from webhook_data is now in metadata and comes back on every event.


Step 5 — Migrate payouts

Triple-A emails the recipient a payout form to collect their wallet address. CoinCircuit saves the recipient once and pays out directly.

Triple-A

POST https://api.triple-a.io/api/v2/payout/withdraw/local/crypto
Authorization: Bearer <token>
Content-Type: application/json

{
  "merchant_key": "mkey-ck9e0srok0000zumg7vx3hpkh",
  "email": "alice@example.com",
  "withdraw_currency": "USDT",
  "withdraw_amount": 50,
  "order_id": "PAYOUT-001",
  "notify_url": "https://your-site.com/webhook"
}

Check balance first with GET /api/v2/payout/balances. Track status with GET /api/v2/payout/withdraw/{payout_reference}. Payout statuses: newconfirmdone (or cancel).

CoinCircuit

POST https://api.coincircuit.io/api/v1/crypto-addresses
x-api-key: sk_live_your_key
Content-Type: application/json

{ "address": "0xRecipientAddress", "chain": "base", "asset": "USDT", "label": "Alice" }
GET https://api.coincircuit.io/api/v1/payouts/fees?asset=USDT&chain=base&amount=50
POST https://api.coincircuit.io/api/v1/payouts
x-api-key: sk_live_your_key
Content-Type: application/json

{
  "asset": "USDT",
  "chain": "base",
  "amount": "50.00",
  "recipientId": "crypto-address-id-from-above"
}

Listen for payout.success or payout.failed webhooks. Track status with GET /api/v1/payouts/{id}.


Step 6 — Check payment status

Triple-A: GET https://api.triple-a.io/api/v2/payment/{payment_reference} with Authorization: Bearer <token>.

CoinCircuit:

GET https://api.coincircuit.io/api/v1/payments/reference/{reference}
x-api-key: sk_live_your_key

The reference is returned when you create the payment. To list all payments:

GET https://api.coincircuit.io/api/v1/payments?page=1&size=20
x-api-key: sk_live_your_key

Step 7 — Test in the sandbox

Triple-A activates test mode with a sandbox: true flag in the request body. CoinCircuit uses a separate base URL with test keys instead — no flag needed.

- https://api.triple-a.io/api/v2
+ https://sandbox-api.coincircuit.io/api/v1

Use the Simulate payment button on the sandbox checkout page to trigger outcomes (full, partial, overpayment, AML failure) without moving real funds. See the Sandbox Environment guide for faucet links and testnet details.