Reopen an expired payment
POST/payments/{id}/reopenRe-arm an expired one-shot payment under a new deadline, with or without a recorded deposit, or re-arm a direct payment whose deploy deadline passed after it was funded. Send one signature per entry of the payment object's payment_options, all under the same deadline (signReopen() from @exodus/checkout-signer builds the body from the payment object). Signatures verify against the business's current factory (factory_address); a payment whose addresses no longer derive from it, after a signer rotation, is refused with factory_rotated. Direct payments sign a fresh deploy and need a deadline at least 30 minutes ahead; two-step payments sign a Reopen message. Every address is registered again, and the deposits already recorded are judged before the response: one that covers an address funds the payment under the new deadline. The response is the payment object. The status in the response reflects the deposits already recorded. Deposits not yet recorded surface after the reopen through the deposit poll: recent ones (the last 6,000 blocks of each chain, about 25 minutes on Arbitrum One and longer on every other chain) typically within about a minute, one cron tick, and older ones typically within about 50 minutes for each day since the payment was created, for a payment option enabled on every supported mainnet (a little under 6 hours for a payment created a week earlier), twice that for a fiat-settled payment with a USDC and a USDT option, and sooner for a payment on fewer chains. These figures assume approximate published block cadences, 12,000 blocks (24 pages of 500) of catch-up per minute left after the poll's own deposit scan, which for the reopened payment starts at its floor, and one reopened payment in the backlog at a time. A refusal is a 409 payment_not_reopenable whose data.cause is one of not_expired (the payment is not expired, a deploy is in flight, or it changed while reopening; the message names which), flagged, checkout_closed, unsupported_method (defensive, no current payment method reaches it), solana_not_supported, factory_rotated (the business rotated its signer, so the addresses no longer derive from its current factory, or the signer configuration changed while reopening; the message names the second case), signer_missing (the business has no merchant signer configured), config_invalid (the business's payment configuration lists no supported chains) or fiat_settlement_not_reopenable (the business settles in fiat, so the quoted token amount is tied to a price lock that a reopen cannot honour; the payment stays expired). A 503 service_unavailable with code: chain_unavailable means the chain could not be reached before anything was written; retry later.
Headers
| Header | Description | Required |
|---|---|---|
| Authorization | Bearer token with your secret API key | yes |
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| id | string | yes |
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
| deadline | integer | yes | |
| signatures | object[] | yes |
signatures
| Field | Type | Required | Description |
|---|---|---|---|
| on_chain_id | string | yes | |
| signature | string | yes |
Example Request
Request
const response = await fetch('https://checkout-api.exodus.com/payments/<id>/reopen', {
method: 'POST',
headers: {
Authorization: 'Bearer sk_live_xxxxxxxxxxxxxxxx',
'Content-Type': 'application/json',
},
body: JSON.stringify({
"deadline": 1798761600,
"signatures": [
{
"on_chain_id": "0x1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b",
"signature": "0x8106eb4a89add4e134b91dfd43edc51e9499df687b4f8fd8f5b51e070326abad033e4ad556d1f34cf65da3e547d388f8672dfb0233f1b2c18e774e121c10030e1c"
}
]
}),
});Responses
| Status | Description |
|---|---|
| 200 | The reopened payment. |
| 400 | Validation error |
| 401 | A reopen signature does not verify |
| 403 | API key lacks the required scope |
| 404 | Resource not found |
| 409 | The payment cannot be reopened |
| 503 | The chain could not be reached; nothing was written |
Response body
| Field | Type | Required | Description |
|---|---|---|---|
| id | string | yes | |
| object | enum: payment | yes | |
| status | enum: client_deposit_pending | client_deposit_detected | client_deposit_confirmed | client_deposit_expired | escrow_confirmed | settlement_pending | settled | failed | refunded | cancelled | yes | |
| payment_method | enum: direct | two_step | yes | |
| detected_asset | string | null | yes | |
| detected_token_symbol | string | null | yes | |
| detected_chain | string | null | yes | |
| token_amount | string | null | yes | |
| token_amount_decimal | string | null | yes | |
| deposit_address | string | null | yes | |
| payer_addresses | string[] | yes | |
| on_chain_id | string | null | yes | |
| factory_address | string | null | yes | |
| payment_options | object[] | yes | |
| created_at | string · date-time | yes | |
| flagged | boolean | yes |
payment_options
| Field | Type | Required | Description |
|---|---|---|---|
| on_chain_id | string | yes | |
| deposit_address | string | yes | |
| chain_family | string | yes | |
| token | string | null | yes |
Example Response
{
"id": "tz4a98xxat96iws9zmbrgj3a",
"object": "payment",
"status": "settled",
"payment_method": "two_step",
"detected_asset": "eip155:1/erc20:0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
"detected_token_symbol": "USDC",
"detected_chain": "eip155:1",
"token_amount": "50000000",
"token_amount_decimal": "50",
"deposit_address": "0x5fbdb2315678afecb367f032d93f642f64180aa3",
"payer_addresses": [
"0x742d35Cc6634C0532925a3b844Bc9e7595f8fE21"
],
"on_chain_id": "0x1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b",
"factory_address": "0x2f1c4e7b9a3d5c8e6f0a1b2c3d4e5f6a7b8c9d0e",
"payment_options": [
{
"on_chain_id": "0x1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b",
"deposit_address": "0x5fbdb2315678afecb367f032d93f642f64180aa3",
"chain_family": "evm",
"token": "USDC"
}
],
"created_at": "2024-01-15T12:30:00Z",
"flagged": false
}Error Response
Error (e.g. 400)
{
"error": {
"type": "validation_error",
"message": "amount must be a positive number",
"param": "amount"
}
}