Point-of-Sale integration

The TripleA API is organised around HTTP calls. Our API has predictable resource-oriented URLs, accepts JSON-encoded request bodies, returns JSON-encoded responses, and uses standard HTTP response codes, authentication, and verbs. It is fully SSL-secured.

TripleA API works with both Bitcoin Testnet and the Bitcoin network. The API ID used determines if the request is for live or test mode.

Payment flow

Integration steps

  1. Your POS system requests payment form data from our API.
    API doc: POST set_addr
  2. Our API responds with a unique bitcoin address from your wallet and an exchange rate, locked for 15 minutes.
  3. Your system checks the address balance every few seconds to detect changes.
  4. Once a payment has been detected, it will remain unconfirmed on the blockchain. A message is displayed on your end to the user saying that their payment has been seen.
  5. Our API detects the transaction and sends a payment update notification to your backend server.
  6. After a period of 10 minutes to 1 hour, the transaction will be validated on the blockchain. Our API detects this and sends another payment update notification.

Authenticating payment updates

The TripleA API will send payment update notifications to your server for every new transaction and for every transaction status change.

To ensure your server does not accept incoming data from malicious third-parties, you can take basic security measures (IP white-listing, SSL certificate checking, …).

We highly recommend adding unique token authentication as follows:

  1. Generate a unique token for each new order.
  2. When requesting payment form data, provide a webhook that includes that token, for example:
  3. For each payment update notification received, look up the stored token for the order ID and make sure they match.

Note: we recommend tokens of at least 24 characters long (192-bit security), generated using a secure random data generating library.

Note: You may add other data to the webhook URL, different or identical across all orders. This is up to you. If you have any doubts, please get in touch to discuss.

Handling payment updates

When are payment update notifications sent?

Whenever a Bitcoin payment is made, a transaction is added to the blockchain which will trigger a payment update notification to be sent to your server (to the payment_update_url you specified in step 1 when requesting the payment page URL).

Each time a Bitcoin transaction gets validated on the blockchain (on average ~10 minutes after a transaction was created), another payment update notification will be sent.

Verifying legitimacy of incoming notifications

When receiving a payment update notification, verify that the response_auth_token for this order matches with the value you received together with the Payment Page URL. (See integration step 2.)

Note that the response_auth_token is unique and different each time, for each order.

Rare edge cases

Should a user by mistake have paid too little, they have the possibility to make another payment to match the total amount owed and paid. In such a case, more than one transaction is made, each triggering a payment update notification when created and again when validated.

Should a user by mistake have paid too much, they’ll have the possibility reaching out and asking for a refund.

Edge cases do not make things more difficult for integration. To see how we keep things clear and accurate you can refer to the section below, Updating order payment statuses.

  "return": "0",
  "status": "OK",

  /* Order payment details. */

  /*** TODO ***/

  /* Collected user data. */
      "name":"Your name",
      "value":"User street<br>Building address"

  /* Server already has this. */
  /* Adding to ease debugging. */

Updating order payment statuses

You update an order’s payment status based on the payment_status and order_status value in a payment update notification.

The payment_status property

Possible values:

  • “unconfirmed”
  • “confirmed”

An order can be made in one or several bitcoin transactions.
As long as at least one transaction is still awaiting blockchain validation, the value will remain “unconfirmed”.

The order_status property

Possible values:

  • “paid”
  • “paid_too_much”
  • “failed_paid_too_little”
  • “failed”
// PHP example

$payment_status = $payment_update->payment_status;
$order_status = $payment_update->order_status;

/* One or more transactions are still unconfirmed. */
if ($payment_status === 'unconfirmed') {
  // Payment has been made, not yet fully validated.
  // Set order payment status to "processing" 
  // (unless it was already marked as such).
elseif ($payment_status === 'confirmed') {
  // Payment has been fully validated.
  // Now it depends on how much has been paid.

  if ($order_status === 'paid') {
    // Mark order payment as "completed".

  elseif ($order_status === 'paid_too_much') {
    // Mark order payment as "completed".
    // Note that user might contact you asking for a refund.

  else {
    // Mark order payment as "failed" and save the order.

    // If the user did pay, but paid too little,
    //  the user may request a refund.

Payment form user experience

Once the Bitcoin payment form is displayed, a Bitcoin payment address is provided together with the amount to be paid and the current exchange rate.

If no payment is made within 15 minutes, the form expires.

If payment made matches the requested amount, a confirmation message is shown. The user is then redirected back to the merchant’s site.

If an insufficient payment is made, the form remains available so that the user can make another payment to the same Bitcoin address. If the 15 minutes of locked exchange rate expires, the user can refresh to get an updated exchange rate and an additional 15 minutes to match the payment. (This continues until the user closes the window or a sufficient amount has been paid.)


Sample code

Code for additional programming languages will be added upon request.

We welcome feedback! Please reach out at support@triple-a.io with your questions or comments.

Please rename file extensions before testing.