CREDIT_CARD and DEBIT_CARD transactions with the Rinne Core API.
Rinne officially supports both 3DS strategies for card payments: session-first and transaction-first.
Transaction endpoints (self vs merchant)
Prerequisites
- Active affiliation for card payments.
ECOMMERCEcapture enabled for online transactions.- One unique
request_idper payment attempt. - Encrypted card values from rinne-js secure components.
Sending card data
Rinne’s API only accepts card credentials in encrypted form. Thecard_data.number, card_data.cvv, card_data.network_token, and card_data.cryptogram values must arrive encrypted — you can recognize encrypted values by their ev: prefix. Requests where one of these fields arrives in plain text are rejected with 400 VALIDATION_ERROR before any processing happens.
These encrypted values come from the rinne-js secure components: the Card Element encrypts the card number and CVV in the customer’s browser, and the wallet elements emit network_token and cryptogram already encrypted. Raw card data never touches your servers.
Required transaction shape
string
Payment provider that processes the transaction, such as
CAPPTA. Optional. When you omit it, Rinne picks the provider from the merchant’s eligible affiliations. See Provider selection.string
required
Idempotency key for creation.
integer
required
Amount in cents.
string
required
Use
ECOMMERCE for card-not-present checkout.string
required
CREDIT_CARD or DEBIT_CARD.object
required
Card payload for card transactions.
Card data rules
- Send exactly one of
card_data.number(raw card entry),card_data.network_token(wallet), orcard_data.card_id(stored card). - Send
numberandcvvas encrypted values from rinne-js. Values that reach the API unencrypted are rejected with400 VALIDATION_ERROR. - With
numberornetwork_token, sendexpiry_month(MM),expiry_year(YYYY),last_digits(the last 4 digits of the card number, exactly 4 digits), andcardholder_nameas it appears on the card. - With
card_id, Rinne resolves those fields from the stored card — see Example: charge a stored card. - For authenticated/tokenized flows, provide cryptogram directly or via
three_d_secure_session_idinjection. - For wallet tokenized flows, include
wallet_type(APPLE_PAYorGOOGLE_PAY).
3DS challenge strategy flags
TransactionCreateRequest supports two optional card-only flags:
boolean
When
true, card transaction creation always goes directly to AWAITING_3DS. Provider authorization is deferred until /authenticate is called with an authenticated 3DS session.boolean
When
true, any path that would require a challenge (risk-triggered or soft-decline-triggered) is refused immediately (REFUSED) instead of transitioning to AWAITING_3DS.Why these flags are useful
With
require_3ds, organizations that already run transaction-first can enforce 3DS for all or selected transactions without adding a separate session-first policy path.
The transaction enters AWAITING_3DS immediately, which is the status you already handle in this flow.
With refuse_on_challenge, challenge-triggered flows do not enter AWAITING_3DS; they stop immediately as REFUSED, so your operation fails fast and carries no challenge-handling workload.
refuse_on_challenge changes only challenge-triggered paths. Transactions that do not require challenge continue normal provider processing.CHALLENGE_NOT_ALLOWED status reason
When refuse_on_challenge is true and a challenge would be required (risk-triggered or soft-decline-triggered), transaction response uses:
status = REFUSEDstatus_reason = CHALLENGE_NOT_ALLOWED
Policy granularity
Because these flags are part of each transaction request, you can apply policies with fine control:- Per merchant (choose behavior by
merchantIdin your backend routing). - Per payment flow (for example, mobile app vs web checkout).
- Per transaction segment (risk tier, issuer group, customer cohort, high-value purchases).
Example: standard card transaction
cURL
Example: charge a stored card
To charge a card you stored on file, send itsid as card_data.card_id in place of the card credentials. Rinne resolves the encrypted number, expiry_month, expiry_year, last_digits, brand, and cardholder_name from the stored card.
cURL
cvv for customer-present payments that re-collect it, and omit it for merchant-initiated payments such as recurring charges. Everything outside card_data — installments, the 3DS challenge strategy flags, and the merchant route — behaves exactly as it does with inline card data.
A card_id that Rinne cannot charge is rejected with 400 VALIDATION_ERROR on the card_data.card_id field, in three cases:
- No such card in scope — the card doesn’t exist, was deleted, or belongs to another company. A card is only chargeable by the company that owns it.
- The card isn’t active — its
statusis notACTIVE. - The card has no
brand— Rinne’s providers require a brand to process a transaction, so a stored card without one can’t be charged.
Example: force transaction-first 3DS
cURL
Example response
Example: fail fast when challenge is not allowed
cURL
Example response
Card statement descriptor
Every card charge appears on the cardholder’s statement as a short line, the soft descriptor. Rinne builds that line from the merchant’s name, and you can add your own text to it per transaction, such as an order number, so the cardholder recognizes the charge instead of disputing it.string
Card transactions only. Your text for the cardholder’s statement, printed after the merchant’s name. Send it at the top level of the create request, next to
amount, not inside card_data. The transaction returns the value exactly as you sent it. An empty string is rejected with 400 VALIDATION_ERROR; omit the field when you have no text.How Rinne composes the statement line
The statement line holds at most 22 characters, in three parts separated by*:
<facilitator>: the identifier of the payment facilitator that processes the sale. Sales through a payment facilitator must carry it, so you cannot change or remove it. It takes 6 characters, plus 1 for its separator.<merchant>: the merchant’sname. Whennameis missing or has no letter A–Z or digit once accents fold, Rinne uses the merchant’s legal name,full_name.<text>: yoursoft_descriptor. When you omit it, or Rinne drops it under the rules below, the line ends at the merchant’s name.
soft_descriptor field on an affiliation does not affect the line.
Rinne applies three rules, in this order, to the merchant’s name and to your text:
- Characters. Accented letters fold to their base letter (
AçaíbecomesACAI) and every letter is uppercased. Rinne then removes spaces,*, letters with no base letter (such asøandæ;ßbecomesSS), letters from other scripts, and every symbol except% $ , . / & ( ) + = < > -. - Text length. Rinne keeps the first 9 characters of your text and cuts the rest without an error. If those 9 characters hold no letter or digit, Rinne drops your text.
- Merchant name length. The merchant’s name gets the characters left over: 15 when no text prints, or 14 minus the length of your text when it does, down to 5 for a 9-character text.
In the fourth row, Rinne removes the space from
Pedido 1234 before the 9-character cut, and the cut then drops the final 4. In the last row, --- has no letter or digit, so Rinne drops it and the line ends at the merchant’s name.
Which transactions print your text
- Card transactions you create with
POST /v1/transactionsorPOST /v1/merchants/{merchantId}/transactionsprint your text, whether the card arrives as a card number, a walletnetwork_tokenor a storedcard_id. - A transaction that moves to
AWAITING_3DSprints the same line once you call/authenticate. You sendsoft_descriptoronly on the create request;/authenticatereuses it.
soft_descriptor confirms the text Rinne received; use the rules above to predict what the cardholder sees.
Example: card transaction with a statement descriptor
cURL
Example response
name is Corner Store Burgers, the cardholder’s statement shows <facilitator>*CORNER*ORDER-42.
Card transaction lifecycle highlights
Retry and idempotency
- Generate one
request_idper attempt. - Reuse same
request_idonly for safe retries of the same attempt. - Use a new
request_idfor a new customer attempt.
Refunding card transactions
Sendrefund_amount equal to the remaining refundable amount for a full refund, or any smaller amount for a partial refund (subject to the rules below).
cURL
Card refund rules
Two independent acquirer rules apply to every card refund. Both are pre-validated server-side and surface asHTTP 400 VALIDATION_ERROR before any provider round-trip.
The acceptance matrix on a transaction approved with approved_amount = 10000 and no prior COMPLETED refund:
Once any refund on the transaction reaches
COMPLETED, every row above is rejected with HTTP 400 (Card transaction has already been refunded; the acquirer does not allow additional refunds), regardless of the day or whether the new request is partial or full.
Card refund response semantics
Card refunds resolve synchronously whenever the acquirer returns a definitive answer:- A successful cancel returns
refund.status = COMPLETEDand the transaction status becomesREFUNDED(full) orPARTIALLY_REFUNDED(partial). - A definitive provider rejection (for example, an acquirer business decline) returns
refund.status = FAILED. Failed card refunds are terminal and are not retried — submit a new refund request to try again. - An uncertain outcome (acquirer 5xx or network timeout) triggers an inline status query against the acquirer. If that resolves the outcome, you still get
COMPLETEDorFAILEDon the same call. Otherwise the refund moves toTIMEOUTand is reconciled asynchronously. - A
PENDING_REFUNDtransaction status on a card transaction means the request was accepted but did not produce a definitive answer; we will retry the (idempotent) cancel.
Integration with rinne-js
- Use Card Element for encrypted card capture.
- Use Guide: Card + 3DS Checkout for frontend 3DS orchestration.
- Use 3D Secure authentication for backend 3DS API contracts.
Your card integration is production-ready when you correctly handle
PROCESSING, AWAITING_3DS, APPROVED, REFUSED, and the CHALLENGE_NOT_ALLOWED status reason.
