Mono Colombia

Payment tokens

What the payer pays to: each method issues its own kind of token.

A payment token is the thing you put in front of the payer: the address the money is sent to. Each payment method defines its own kind of token. Not every token is an address: PSE's is a link that sends the payer to their own bank. On a template, payment_tokens is a per-method map, with one entry per accepted method that issues a template token. An intent has a single method, so its payment_tokens is that method's token directly.

Today tokens come in three forms. Bre-B issues a key (in Colombia, a llave: a short alias like a cellphone number or a code, registered in the Bre-B directory) and an optional QR code that encodes the same destination, so the payer can scan instead of type. PSE issues a payment link: the page where the payer approves the payment at their bank.

Templates and intents both carry tokens, and the difference between them is the difference between a poster on a wall and a ticket with your name on it: a template's token is shared by every payer; an intent's token identifies one expected payment.

Where the token lives

Ownerpayment_tokens is…Bre-BPSE
TemplateA per-method mappayment_tokens.co_brebNo entry: PSE has no template token
IntentThe token of its single methodpayment_tokens (key and QR)payment_tokens.pse_url

The Bre-B token

The Bre-B token has three parts (on a template, under payment_tokens.co_breb):

FieldWhat it is
key_typeThe kind of key: cellphone, email, alphanumeric, identification, or merchant_code. Assigned by the network when the key is registered. It is not known at creation time.
key_valueThe key itself: what the payer enters in their bank's app to address the transfer.
qrThe QR generated for the key, when one was requested (create_qr: true).

The QR object carries everything needed to display or embed it: the EMV payload in plain text (emvco), a rendered image in base64 (image, with image_width and error_correction_level), the provider's payment_id, its usage_mode, and, for dynamic QRs, the fixed amount and expires_at.

You can request a custom key alias with method_params.co_breb.custom_key_value, and override the QR's merchant data (merchant_category_code, receiver_city, receiver_postal_code) with qr_code_data. If omitted, Mono generates the key and uses your program's configuration.

Key per template or key per intent

method_params.co_breb.key_per decides at which level the Bre-B key is minted, and it shapes the whole collection model:

key_perToken lives onQR kindPayments are…
template (default)The templateStatic: multiple use, no amountAnonymous until they arrive: any payer, any number of payments.
intentEach intentDynamic: single use, fixed amount, expiresReferenced: each payment maps back to the intent that expected it.

A template with key_per: "template" receives payments directly. It cannot host intents, because a shared key cannot tell which intent a payment belongs to. A template with key_per: "intent" has no key of its own (payment_tokens stays null): every key is minted when an intent is created. A templateless intent gets its own dedicated key too.

The PSE token

A PSE intent's payment_tokens has a single field:

FieldWhat it is
pse_urlThe page where the payer approves the payment at their bank. Redirect the payer there; their bank sends them back to the payin's redirect_url.

Each PSE intent gets its own link, for one payment attempt. A template has no PSE token, so a template that offers only PSE keeps payment_tokens as null. See PSE for the whole redirect flow.

Token setup is asynchronous

Registering a token with the method's network takes a callback round-trip, which is why templates and intents are born in created. Neither is payable until the token is confirmed:

  • The template moves to ready and fills payment_tokens when every pending token is confirmed (announced by payin_template_ready).
  • The intent moves to ready_to_pay and fills payment_tokens (announced by payin_intent_ready_to_pay).

If registration exhausts its retries, the template ends failed (key_registration_failed, key_already_registered) or the intent ends failed (key_registration_failed, payment_token_registration_failed).

PSE generates its link in a single call, with no callback, but only after the intent is created. The intent still starts in created and reports the link at ready_to_pay, like Bre-B. A link that cannot be generated fails the intent and its payin with the same reason: risk_control, provider_unavailable, internal_error, or unknown.

Tokens are torn down

A token only lives while its owner can be paid. When a template is pruned or deleted, or an intent reaches a final state (successful, failed, or expired), Mono deregisters its tokens with the method's provider. The owner's prune_state / prune_reason fields track that teardown. PSE links are not torn down: each one belongs to a single payment attempt.

Next steps

  • Bre-B: the rail behind the keys and QRs.
  • PSE: the rail behind the payment link.
  • Intents: where dynamic tokens and PSE links live.

On this page