API versionv1 live

Charge subscriber

Charge a subscriber's saved card, without the payer. We send the charge to your acquirer as a merchant-initiated transaction, and answer when it is authorized, and captured unless you say not to, or when it has failed.

Request

POST https://api.gjold.com/v1/subscribers/{subid}/charge
  • {subid} is the subscriber's "id", from seq.

The key needs the charge permission.

Headers

HTTP header Description
Authorization

Required. Your API key, after Basic and base64-encoded. See Authentication.

Idempotency-Key

A key you make up for this request, and send again with each retry of it, so that the request is done once however often it arrives. Up to the 4 KiB of all headers, and kept for 24 hours. See Idempotency.

Always send one with a charge: see Retrying.

Fields

An address you send is this charge's, in place of the subscriber's whole, and one you leave out is the subscriber's.

Field Type Description
items array, required

What the payer pays for: one or more items, whose totals add up to the amount to pay. All in one currency, and together more than 0.

items[].total amount, required

The price of the item, its whole quantity together, as a string with its currency, as "199.90 DKK". At least 0. Decimals beyond the currency's minor unit are cut off: "10.999 DKK" is 10.99 DKK. See Amounts.

items[].quantity integer

How many, a positive integer. The default is 1. It is for your records and the dashboard: the "total" is already for all of them.

A number, not a string.
items[].name string

The name of the product or service, for your records and the dashboard. The payment window does not show it.

items[].sku string

Your SKU for the product. We accept it, but don't store it, so seq does not return it.

orderid string Your ID for what the charge pays, such as the subscription and its period. Seq returns it with the charge, which is how you match the charge to your records, and the acquirer passes it to the payer's bank, as for a payment link. Optional, but give it, as a string: a number is ignored.
autocapture boolean Capture the charge as soon as it is authorized, which is the default, true, unlike for a payment link. With false, it is only authorized, and you capture it later. If a capture fails, the authorization is voided, and the charge fails.
billing object The billing address for this charge. See Addresses.
shipping object The shipping address for this charge.

Every string field must be a JSON string here, unlike in Create payment link, and a field of another type may be ignored rather than refused. After the JSON object, the body may have only white space.

Example

Charge a month of a subscription, with a key made for that month and its first attempt:

curl https://api.gjold.com/v1/subscribers/19/charge \
    -u {apikey} \
    -H 'Idempotency-Key: sub19-2026-12-try1-7f3a9c2e5b1d4f60' \
    -d @charge.json
{
    "orderid": "M-7192-2026-12",
    "items": [
        {
            "name": "Membership, December",
            "total": "99 DKK"
        }
    ],
    "autocapture": true
}

Response

We answer 200 OK with the charge, a transaction, once the acquirer has authorized it, and captured it with "autocapture":

Field Type Description
type string Always "charge".
id integer The transaction's ID, by which you can capture or refund it.
totals.authorized amount What the bank authorized: the items' total.
{
    "type": "charge",
    "id": 1043,
    "totals": {
        "authorized": "99 DKK"
    }
}

Seq returns the whole transaction too, as a charge, with its act of capture if it had "autocapture". A charge is never partly authorized: if the bank approves only part of the amount, we void that, and the charge fails with 500 charge failed: {acquirer} error: partial authorization.

Headers

HTTP header Description
Idempotency-Status: OK Sent with every answer to a request that has an Idempotency-Key, the first and its replays alike. See Idempotency.

Timing

A charge waits for the acquirer, once to authorize, and again to capture: a second or two, as a rule, but up to 1 minute per call with Clearhaus, or 10 minutes with Nets, when the acquirer is slow. See Timeouts.

Errors

Declines

Every charge that fails answers 500, with a message that starts with charge failed: and names the party that failed, the acquirer or one of ours, and its reason:

HTTP/1.1 500 charge failed: nets error: not sufficient funds
HTTP/1.1 500 charge failed: clearhaus error: card expired
Status and message Description
500 charge failed: {acquirer} error: {why} The acquirer declined the charge, did not answer in time, or approved only part of it. {acquirer} is nets or clearhaus, and {why} its reason.
500 charge failed: subscriber database error: {why} The shop has no subscriber with that {subid}, or it has no saved card. Check the {subid}, or have the payer renew the subscriber.
500 charge failed: {why} Anything else that stopped the charge, as items that are missing or add up to 0.

v1 does not say whether a decline may pass. {why} is the acquirer's reason, which acquirers word differently, so read it for your logs and support, not to branch on. Treat a card that has expired, been blocked, or been reported lost, or a payer who stopped your charges, as final, and have the payer renew the subscriber; try anything else again later, with a new key, but not the same day. See When a charge fails and the knowledge base's Declined charges.

A declined charge leaves no transaction, so seq does not return it; record it yourself. When the acquirer did not answer in time, the payer's bank may have authorized the charge without our knowing. There is then no transaction to capture, so the bank releases the amount after some days; until then, the payer may see it held. Waiting a day before the next try keeps two holds from piling up.

Other errors

Besides the errors that any request can give, which Errors lists:

Status and message Description
400 invalid json in {where}: {why} The body is not JSON, an item's total can't be read, has no currency, or is in another currency than the items before it, or there is more than white space after the object. See Invalid JSON.
500 invalid item total currency An item's total is in a currency that is not in the ISO 4217 table.
500 {why} We failed. If the charge reached the acquirer, it may have gone through: synchronize before you try again.

Retrying

If the connection breaks before you have the answer, send the request again with the same Idempotency-Key: a charge still running is waited for, and a finished one answers as before, a failure included. A new attempt after a failure needs a new key. Without a key, you can't know whether the charge went through, and a second request charges the payer twice. For the same reason, never charge the same subscriber twice at once.

See also

Charging subscribers, and in the knowledge base, Stored credentials and Declined charges.