Payment Sequencing
Default Payment Sequence
When you call Ensure Refunds without specifying a sequence of OrderPaymentSummaries, it distributes the refund between the OrderPaymentSummaries belonging to the OrderSummary according to this logic. However, if multiple OrderPaymentSummaries have equal amounts, their selection order is random.
- If a credit memo is specified, identify OrderPaymentSummaries with captured amounts that
were applied to the corresponding invoice.
- Examine those OrderPaymentSummaries. If one has a captured amount matching the credit memo amount, apply the refund to that payment.
- If no exact match is found, look for OrderPaymentSummaries with captured amounts greater than the credit memo amount. If any exist, apply the refund to the smallest.
- If no greater amounts are found, traverse the OrderPaymentSummaries in order of captured amount, from largest to smallest. Apply the refund to them until it’s fully applied.
- If an excess funds amount is specified, identify OrderPaymentSummaries with captured
amounts that weren’t applied to any invoice.
- Examine those OrderPaymentSummaries. If one has a captured amount matching the excess funds amount, apply the refund to that payment.
- If no exact match is found, look for OrderPaymentSummaries with captured amounts greater than the excess funds amount. If any exist, apply the refund to the smallest.
- If no greater amounts are found, traverse the OrderPaymentSummaries in order of captured amount, from largest to smallest. Apply the refund to them until it’s fully applied.
Specify the Sequence of OrderPaymentSummaries
When you call Ensure Refunds, you can sequence the distribution of the refund by including the isAllowPartial and sequences values.
- isAllowPartial
- Controls what happens when the distributed amounts that you specify don’t cover the full amount. If the value is true, the remaining amount is skipped. If it belongs to a credit memo, it remains on the credit memo. If the value is false, the default logic is applied to the remaining amount.
- sequences
- An ordered list of paired amounts and OrderPaymentSummaries. Each amount is refunded to the OrderPaymentSummary paired with it. The process traverses this list in order and stops when it has refunded the full amount.
Custom Default Sequence Apex Example
You can implement your own default payment sequence by wrapping Ensure Refunds in code that generates the isAllowPartial and sequences values. Here’s an example using Apex that tries to refund to DigitalWallet payment methods before any other payment type.
- Collect all OrderPaymentSummaries associated with the OrderSummary. Identify the payment type using the record ID’s key prefix.
- Refund $10 to each OrderPaymentSummary that’s a DigitalWallet until the full amount has been refunded.
- If an amount remains, refund it according to the default logic.
You can customize which payment type to refund first by changing the amount associated with each type’s key prefix. Because the list is ordered, you could, for example, refund $10 to each DigitalWallet, then $10 to each CardPaymentMethod, and then apply the default logic to any remaining amount.
public class CreateSequenceOPSRefundsInvocable {
@InvocableMethod(label='EnsureRefunds with Sequenced OrderPaymentSummaries')
public static void createSequenceOrderPaymentSummaryList(List<String> creditMemoIds) {
// When applying refunds, prefer DigitalWallets
// DigitalWallet Key Prefix: 1DW
// CardPaymentMethod Key Prefix: 03O
// AlternativePaymentMethod Key Prefix: 8Z7
Map<String, Double> sequencePrefixAndAmounts= new Map<String, Double>();
sequencePrefixAndAmounts.put('1DW', 10.0);
sequencePrefixAndAmounts.put('03O', 0.0);
sequencePrefixAndAmounts.put('8Z7', 0.0);
// Always disallow partial refunds -- if the sequence doesn't cover the full refund, then apply the default logic to the remaining amount
Boolean isAllowPartial = false;
Map<String, Double> sequenceOPSAndAmounts = new Map<String, Double>();
ConnectApi.EnsureRefundsAsyncInputRepresentation ensureRefundsInput = new ConnectApi.EnsureRefundsAsyncInputRepresentation();
ensureRefundsInput.creditMemoId = creditMemoIds.get(0);
ensureRefundsInput.sequences = new List<ConnectApi.SequenceOrderPaymentSummaryInputRepresentation>();
ensureRefundsInput.isAllowPartial = isAllowPartial;
// Get the Id and ReferenceEntityID (OrderSummaryID) From the Credit Memo
CreditMemo cm = [SELECT Id, ReferenceEntity.Id FROM CreditMemo WHERE Id = :creditMemoIds Limit 1];
string orderSummaryId = cm.ReferenceEntityId;
// Get list of OrderPaymentSummaries from the orderSummaryID
List<OrderPaymentSummary> orderPaymentSummaries = [SELECT Id, PaymentMethod.Id, Type FROM OrderPaymentSummary WHERE OrderSummary.Id = :orderSummaryId];
Map<Id, OrderPaymentSummary> orderPaymentSummariesMap = new Map<Id, OrderPaymentSummary>(orderPaymentSummaries);
//Loop through the sequence of payment methods prefixes
for(String keyPrefix: sequencePrefixAndAmounts.keySet()){
// Loop through orderPaymentSummaries creating a map of sequence
for(OrderPaymentSummary ops: orderPaymentSummaries){
string paymentMethodId = ops.PaymentMethod.Id;
if(paymentMethodId.startsWith(keyPrefix)){
sequenceOPSAndAmounts.put(ops.Id, sequencePrefixAndAmounts.get(keyPrefix));
orderPaymentSummariesMap.remove(ops.Id);
}
}
}
// Create the sequence list and add it to the EnsureRefundsAsync input
for(String orderPaymentSummarySequence: sequenceOPSAndAmounts.keySet()){
ConnectApi.SequenceOrderPaymentSummaryInputRepresentation request = new ConnectApi.SequenceOrderPaymentSummaryInputRepresentation();
request.amount = sequenceOPSAndAmounts.get(orderPaymentSummarySequence);
request.orderPaymentSummaryId = orderPaymentSummarySequence;
ensureRefundsInput.sequences.add(request);
}
ConnectApi.EnsureRefundsAsyncOutputRepresentation result = ConnectApi.OrderSummary.ensureRefundsAsync(orderSummaryId, ensureRefundsInput);
}
}