Payment protocol reference

The signed requests, responses, fields, and reliability rules for payment protocol 1.

This reference describes payment protocol 1. Tringify selects the protocol for each declaration revision. Provider operations are HTTPS POST requests with JSON request and response bodies.

Signed request headers#

Header

Purpose

X-Tringify-Signature

t=<timestamp>,v1=<signature>: the same signature as app webhooks.

X-Tringify-Timestamp

Unix timestamp included in the signature.

X-Tringify-Request-ID

Unique identifier for this delivery attempt.

X-Tringify-Store-ID

Store that owns the connection.

X-Tringify-App-ID

App receiving the request.

Read the raw request bytes before parsing them. Compute the lowercase hexadecimal HMAC-SHA256 of the timestamp, a period and the raw request bytes (<timestamp>.<raw body>) with the app's signing key, and compare it in constant time with each v1 value in the header; accept the request if any one of them matches. The key is used as the text shown in the Developer Portal, all 64 characters; do not hex-decode it. Check that t equals X-Tringify-Timestamp and reject a timestamp more than 5 minutes from your clock, in either direction, before creating provider state. This is the same check as for app webhooks.

Worked signature example#

With this signing key, timestamp and body, a correct verifier computes exactly this header. Use it as a test vector.

signature-example.txt text
Signing key:          9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08
X-Tringify-Timestamp: 1760000000
Raw body:             {"refund_id":"refund_01","session_id":"session_01","store_id":"store_01","order_id":"order_01","provider_transaction_id":"pay_01","amount":"499.00","currency_code":"USD"}
Signed string:        1760000000.{"refund_id":"refund_01","session_id":"session_01","store_id":"store_01","order_id":"order_01","provider_transaction_id":"pay_01","amount":"499.00","currency_code":"USD"}
X-Tringify-Signature: t=1760000000,v1=c4009bdf4ba267d7968cf935a86306eb8a1fa1d988104bc18472f6447f959218

Rotate the signing key#

Today: immediate replacement. Generating a new key on the app's Webhooks page replaces the old one at once for every installation and every request Tringify signs. Provider calls and lifecycle events switch to the new key at once; other event deliveries follow within moments. Tringify stops signing with the old key immediately, so your verifier rejects every request until it has the new key. Rotate when traffic is low, and deploy the new key to your service immediately after generating it.

  1. Prepare the deployment that updates the signing key, so it is ready to run.

  2. Generate the new key and copy it; it is shown once.

  3. Deploy it at once. Requests rejected in the gap are not lost permanently: refund requests and lifecycle events are retried, while a charge request that fails is a failed attempt the shopper can retry.

  4. Confirm with Send test webhook that the new key verifies, then check for refunds left pending during the gap and reconcile them.

Coming: a 72-hour overlap. In a forthcoming change, Tringify will keep signing with the previous key for 72 hours after a rotation. During that time X-Tringify-Signature carries one v1 value per key, current first (t=<timestamp>,v1=<current>,v1=<previous>), so a service still holding either key keeps verifying while you deploy the new one. Until that change ships, rotation is the immediate replacement above.

Build your verifier for both now: accept a request when any v1 value in the header matches the signature you compute, and never assume the header has exactly one. A verifier that reads only the first v1, or rejects a header with more than one, will reject valid requests once the overlap ships.

Time limits#

Step

Limit

One request to your connection-check, charge, or refund endpoint

30 seconds, from connecting to reading your full response. Answer with a 2xx status: redirects are refused, a status of 400 or above counts as no answer, and the body of any other status is read as your response.

Tringify waiting for a charge or refund outcome

40 seconds. Always longer than the request, so an answer within 30 seconds is never discarded.

Your calls to the payment provider

Must finish within the 30 seconds request. Give them a shorter deadline of your own; for a refund whose outcome you cannot confirm in time, answer pending.

A charge request is sent once. A refund request is repeated as described in Refund retries and deadlines.

Connection check request#

Field

Type

Required

Description

binding_id

string

Yes

Identifies the connected merchant account.

store_id

string

Yes

Identifies the store that owns the connection.

app_id

string

Yes

Identifies your app.

service_type

string

Yes

The requested provider service; payment for this contract.

connection_key

string

Yes

Your stable key for this merchant connection.

declaration_revision_id

string

Yes

The approved declaration this connection must follow.

Connection check response#

Field

Type

Required

Description

status

string

Yes

Whether the account is ready, needs merchant action, or cannot be used.

status_reason_code

string

No

Optional stable reason code supplied by your app.

mode

string

Conditional

Whether the connected credentials are test or live.

health

string

Conditional

Whether the ready account is healthy or temporarily degraded.

account_country

string

Conditional

ISO country code for the connected provider account.

account_hint

string

No

Safe, masked text that helps the merchant recognize the account.

presentment_currencies

array of string

Conditional

Complete ISO currency-code set for the connected account. Required when status is ready.

Start-payment request#

Field

Type

Required

Description

session_id

string

Yes

Tringify payment-session ID and idempotency key.

protocol_version

string

Yes

Payment protocol version for this request.

declaration_revision_id

string

Yes

Approved declaration revision that governs this request.

store_id

string

Yes

Store accepting the payment.

market_id

string

Yes

Market whose payment-provider assignment selected this connection.

store_name

string

No

Optional store name for the provider experience.

order_id

string

Yes

Order being paid.

order_number

string

No

Optional merchant-facing order number.

amount

string

Yes

Exact decimal amount as a string; never a floating-point number.

currency_code

string

Yes

ISO currency code for the amount.

test

boolean

Yes

Whether this payment uses a provider-attested test connection and must not move real funds.

customer_email

string

No

Customer email when available and needed for payment.

customer_phone

string

No

Customer phone when available and needed for payment.

billing_address

object

No

Normalized billing address when available and needed for payment.

return_url

string

Yes

Tringify URL to return to after the provider experience.

cancel_url

string

No

Optional Tringify URL used when the customer cancels.

expires_at

ISO 8601 timestamp

Yes

End of the shopper's attempt in checkout. The provider may still settle the payment later; see Expiry and late settlement.

Start-payment response#

Field

Type

Required

Description

success

boolean

Yes

Whether the provider payment was created or retrieved.

launch_url

string

Conditional

Provider-hosted HTTPS URL. A successful response requires exactly one of launch_url or browser_action.

browser_action

object

Conditional

Separately reviewed browser-adapter payload. A successful response requires exactly one of launch_url or browser_action.

provider_session_id

string

No

Optional provider-owned identifier for this attempt.

error_code

string

No

Stable, non-sensitive error code when the request is rejected.

error_message

string

No

Safe developer-facing detail when the request is rejected.

Refund request#

Field

Type

Required

Description

refund_id

string

Yes

Tringify refund ID and idempotency key.

session_id

string

Yes

Original Tringify payment-session ID.

store_id

string

Yes

Store requesting the refund.

order_id

string

Yes

Order being refunded.

provider_transaction_id

string

Yes

Provider transaction previously reported for the payment.

amount

string

Yes

Exact refund amount as a decimal string.

currency_code

string

Yes

ISO currency code for the refund amount.

reason

string

No

Optional merchant-provided reason for the refund.

Refund response#

Field

Type

Required

Description

success

boolean

Yes

Whether the refund request was accepted.

provider_refund_id

string

No

Optional provider-owned refund identifier.

status

string

Conditional

Required for an accepted request: refunded, pending, or failed.

error_code

string

No

Stable, non-sensitive error code when the request fails.

error_message

string

No

Safe developer-facing detail when the request fails.

Reliability rules#

Treat session and refund IDs as idempotency keys and reject a reused ID whose immutable request values changed. Return the original provider object for a repeated matching request. For a start-payment request, checkout must receive exactly one valid browser handoff before the customer can continue. When one provider object can contain several attempts, persist attempts separately and derive the Tringify session from the provider object's complete authoritative state. Reconcile uncertain and late results through the Store API. For a repeated refund request, return the latest state of the same provider refund instead of creating another refund. Use native provider idempotency with the same immutable refund ID; lookup followed by creation alone does not protect concurrent retries. A timeout or unavailable response is not proof of failure. Keep the refund pending and reconcile it until the provider confirms a terminal result. Preserve unresolved refund records across temporary disconnections. Validate provider-specific amount steps in addition to currency decimals, and never silently round a requested payment or refund.

Sensitive data#

Provider requests and Store API callbacks must not include card numbers, CVV or CVC values, cardholder data, wallet cryptograms, PINs, API keys, access tokens, or merchant credentials.