Charges

Understand how charges are created and the role they play in the order process

A Charge object represents the amount to charge to a payment source as well as the state of the charge.

When an order is submitted, Digital River must get authorization from the payment provider to create a Charge for the amount indicated. For credit cards, once an authorization has been approved, the charge typically remains valid for seven days.

During the lifecycle of a Charge, one or more Capture, Cancel, and Refund objects can be created. The amount specified in each of these objects, when aggregated, ultimately determines how much the customer's payment method is credited or debited.

The Charge object is an enduring record of all the captures, cancels, and refunds that occurred. As a result, you have access to permanent data on when these objects were created and their amounts.

How a charge is created

A Charge object is created from a payment source. When you submit a create Order request, Digital River uses the source data to create a Charge and associate it with the order. The Order response you receive includes a charges array that contains a single Charge object.

The Digital River API does not currently support split tender. As a result, the charges array will only contain one Charge object.

Once a Charge is created, the Source object used to create it is referenced by the sourceId attribute.

In the following sample response, a valid credit card was used to create the source that generated the charge. Since this is a synchronous payment method, the Charge is returned immediately in a capturablestate (assuming it is approved by the card networks). For asynchronous payment methods, where additional customer action is required, the Charge is typically returned in a pending state.

To be notified of when a Charge transitions to either of these states, you can set up webhooks to receive order.charge.capturable and order.charge.pending events.

The amount attribute below only signifies the initial, authorized charge amount. Any cancellations or refunds issued on the order will reduce the actual amount debited from the customer's payment source.

{
"id": "178727320336",
...
"state": "accepted",
...
"fraudState": "passed",
...
"charges": [
{
"id": "b469adb4-d9d1-4baa-80b1-e55c26feb4e8",
"createdTime": "2020-07-02T20:10:47Z",
"currency": "USD",
"amount": 145.16,
"state": "capturable",
"captured": false,
"refunded": false,
"sourceId": "9d9cff59-2b43-4b0a-a171-f49a243e5ea8"
}
],
...
}

Once created, you can then retrieve a Charge by supplying its unique identifier as a path parameter.

How captures, cancels, and refunds function

A Capture object indicates that Digital River is in the process of settling an authorization. In other words, it represents how much the customer's payment source will likely be debited. Some sources require that the full amount of the charge be captured at once, while others allow multiple captures.

A Cancel reduces the amount available to be captured. In these cases, the customer's payment source is neither debited or credited. Whether full or partial cancellation is available depends on the underlying payment source.

And finally, a Refund represents funds returned to the customer. Once funds have been captured by a charge, all or part of the captured amount can be refunded.

All three of these objects start in a pending state and then either transition to complete or failed. And for each of these state transitions, a corresponding event is created. For example, when a Cancel is created by Digital River, an order.charge.cancel.pending event is emitted, followed by either anorder.charge.cancel.complete event or an order.charge.cancel.failed event.

Captures

Once a Charge enters a capturable state, it can be used by Digital River to create a Capture. Digital River cannot capture a charge until it is capturable.

Digital River creates a Capture whenever you create a Fulfillment that specifies a quantity in the request. Digital River does not attempt to capture the cost of the specified items (plus the relevant portion of any taxes, shipping costs, duties, and fees) until you notify us that you've fulfilled the order.

You can be notified that the capture process has begun by listening for order.charge.capture.pending events.

The following POST request shows a partial fulfillment of an order.

cURL
cURL
curl --location --request POST 'https://api.digitalriver.com/fulfillments' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer <API_key>' \
"orderId": "178728710336",
"items": [
{
"itemId": "98014170336",
"quantity": 1
},
{
"itemId": "98014180336",
"quantity": 2
}
]
}'

In this example, you fulfilled two separate items, so the response will contain one or more Capture objects that contain a unique identifier. A reference to each exists in the captures array and each represents a captured amount.

As you fulfill goods, the capturedAmount attribute at the order-level provides a running figure of the total amount collected.

Since the payment source is a credit card, a synchronous source type, the captures briefly have a state of pending before transitioning to complete. Once a Capture has made this transition, Digital River triggers an order.charge.capture.complete event.

Also note in the following sample response that the Order remains in a state of accepted because it's only been partially fulfilled but captured becomes true.

JSON
JSON
{
"id": "178728710336",
...
"totalAmount": 145.16,
...
"state": "accepted",
...
"charges": [
{
"id": "c23d5317-3410-4abb-af86-8c8570b9dc90",
"createdTime": "2020-07-02T21:43:30Z",
"currency": "USD",
"amount": 145.16,
"state": "capturable",
"captured": true,
"captures": [
{
"id": "075224cd-0d07-440f-9c16-a17f16cb9692",
"createdTime": "2020-07-02T21:50:35Z",
"amount": 64.52,
"state": "complete"
},
{
"id": "966387d3-3693-4d35-bf8e-9f190b58e2a8",
"createdTime": "2020-07-02T21:50:35Z",
"amount": 24.2,
"state": "complete"
}
],
"refunded": false,
"sourceId": "9d9cff59-2b43-4b0a-a171-f49a243e5ea8"
}
],
...
"capturedAmount": 88.72,
"cancelledAmount": 0.0,
...
}

Cancels

You can also cancel part of a charge. When you create a Fulfillment that specifies a cancelQuantity, one or more Cancel objects are generated by Digital River and persisted in the Charge.

cURL
cURL
curl --location --request POST 'https://api.digitalriver.com/fulfillments' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer sk_test_68c0e36d7917474ca077f4c98d26050e' \
--header 'Cookie: visid_incap_1638494=62Z4/PTETRmkZkZ3SlxVTDpHtF4AAAAAQUIPAAAAAACGQO0FdnZ7zb8/xIqdgyAa; nlbi_1638494_1914372=Z42yObNSoGk6L2yCgd4GMQAAAABWUIZqfIBfEeL9zXoXYItv; nlbi_1638494=snJkNq6qmm2pgVfVgd4GMQAAAABsawcrSY0/mGfpdpB3gDvN; incap_ses_513_1638494=cBdber641kLOZDB65ooeB/dU/l4AAAAAYoHk9U3rNL+slY8gybF0Fg==' \
--data-raw '{
"orderId": "178728710336",
"items": [
{
"itemId": "98014170336",
"cancelQuantity": 1
},
{
"itemId": "98014180336",
"cancelQuantity": 1
}
]
}'

The same Order now contains two Cancel objects in the cancels array. Each of them contain their own unique identifier and a createdTime. Since the Order is fulfilled and all possible charges have either been captured or cancelled, the state of the Order switches to complete.

{
"id": "178728710336",
...
"totalAmount": 145.16,
...
"state": "complete",
...
"charges": [
{
"id": "c23d5317-3410-4abb-af86-8c8570b9dc90",
"createdTime": "2020-07-02T21:43:30Z",
"currency": "USD",
"amount": 145.16,
"state": "processing",
"captured": true,
"captures": [
...
],
"refunded": false,
"cancels": [
{
"id": "5c13a17a-8c30-4550-be27-ddd84894b45e",
"createdTime": "2020-07-02T21:54:51Z",
"amount": 32.26,
"state": "complete"
},
{
"id": "58d9ff72-d043-4e00-9552-fb6dd8144a6c",
"createdTime": "2020-07-02T21:54:51Z",
"amount": 24.18,
"state": "complete"
}
],
"sourceId": "9d9cff59-2b43-4b0a-a171-f49a243e5ea8"
}
],
...
"capturedAmount": 88.72,
"cancelledAmount": 56.44,
"availableToRefundAmount": 88.71,
...
}

Refunds

Refunds against the charge are contained in the refunds array. However, there is not a one-to-one relationship between a Refund object at the charge-level and a Refund object at the refund-level. In other words, Refund objects at these levels do not share id values. This is because, for batch processing purposes, charge-level refunds are often aggregated.

The following refunds in this example are all associated with the same order.

Refund-level
Order-level
Charge-level
Refund-level

This GET request queries refunds by orderId.

curl --location --request GET 'https://api.digitalriver.com/refunds?orderId=178728710336' \
--header 'Authorization: Bearer <API_key>' \

In the data array, the response returns all the refunds issued on this order. In this example, only a single refund has been issued and it contains a unique id.

{
"hasMore": false,
"data": [
{
"id": "re_40af9cbd-0f38-4362-9e24-c6338f3795c4",
...
"items": [
{
"amount": 21.51,
"quantity": 1,
"refundedAmount": 21.51,
...
},
{
"amount": 32.26,
"quantity": 1,
"refundedAmount": 32.26,
...
}
],
"orderId": "178728710336",
...
"refundedAmount": 53.769999999999996,
...
}
]
}
Order-level

This GET request retrieves the order by using its unique identifier.

curl --location --request GET 'https://api.digitalriver.com/orders/178728710336' \
--header 'Authorization: Bearer <API_key>' \

Once again, the refunds array contains only a single element. However, despite being associated with the same order and being for nearly the same amount, this Refund object does not reference the same object as the one at the Refund-level.

{
"id": "178728710336",
...
"charges": [
{
"id": "c23d5317-3410-4abb-af86-8c8570b9dc90",
...
"refunded": true,
"refunds": [
{
"createdTime": "2020-07-02T22:39:00Z",
"amount": 53.77,
"state": "complete"
}
],
...
}
Charge-level

This GET request uses the charges.id value from the order to retrieve its associated charges.

curl --location --request GET 'https://api.digitalriver.com/charges/c23d5317-3410-4abb-af86-8c8570b9dc90' \
--header 'Authorization: Bearer <API_key>' \

In the response, an array of Refund objects is returned, each with a unique id. Note that the id values at the refund-level and charge-level are not the same.

{
"id": "c23d5317-3410-4abb-af86-8c8570b9dc90",
...
"orderId": "178728710336",
...
"refunded": true,
"refunds": [
{
"id": "5e32af54-aff5-49a8-b72e-37aa8e3d03af",
"createdTime": "2020-07-02T22:39:00Z",
"amount": 53.77,
"state": "complete"
}
],
...
}

Observe that the totalAmount of the Order and the amount of the Charge remain unchanged from when they were initially assigned. The actual amount the payment source is debited or credited is reflected by the capturedAmount and refundedAmount, respectively.

{
"id": "178728710336",
...
"totalAmount": 145.16,
...
"refundedAmount": 53.77,
"state": "complete",
...
"charges": [
{
"id": "c23d5317-3410-4abb-af86-8c8570b9dc90",
"createdTime": "2020-07-02T21:43:30Z",
"currency": "USD",
"amount": 145.16,
"state": "processing",
"captured": true,
"captures": [
...
],
"refunded": true,
"refunds": [
{
"createdTime": "2020-07-02T22:39:00Z",
"amount": 53.77,
"state": "complete"
}
],
"cancels": [
...
],
"sourceId": "9d9cff59-2b43-4b0a-a171-f49a243e5ea8"
}
],
...
"capturedAmount": 88.72,
"cancelledAmount": 56.44,
"availableToRefundAmount": 34.94,
...
}

Note that the Charge is processing despite all charge operations being in a complete state. This is a known issue and is being addressed.

Handling failures

When a charge fails, it goes into a failed state. The same is also true for captures, cancels, and refunds.

Each of these objects contain the failureMessage and failureCode attributes. They both provide a reason for the failure, with one containing a message and the other a code.

The charge lifecycle

A Charge has a defined lifecycle and can be in any one of the following states: pending, capturable, processing, complete, cancelled, or failed.

Additionally, when the state changes, an Event is usually created that represents that change. For example, when the state changes from pending to capturable an order.charge.capturable event is emitted.

The state values for a successful Charge (i.e. the happy path) are pending > capturable > processing > complete .

The following table lists the enumerated state values and their associated events in the approximate order they might occur in the lifecycle:

1) When the Charge is...

(2) the state transitions to...

(3) and a ... event is emitted.

awaiting authorization

pending

order.charge.pending

authorized

capturable

order.charge.capturable

captured in full and there are no pending charge operations

complete

order.charge. completed

cancelled

cancelled

order.charge.cancelled

For different success and error scenarios, you can create tests to determine whether your integration returns the expected state value.