Follow

Refunding and Voiding Transactions

A very common question is, “What is the difference between a Refund and a Void?”. Let’s start with the definitions:

  1. REFUND – Creates a brand new transaction that returns funds to the account holder
  2. VOID – Updates an existing transaction so that funds are not captured from the account holder

Rules for a REFUND:

  • You can only refund an account holder as much as they have been charged in the previous 12 months.

Rules for a VOID:

  • You can only void a transaction before it is settled (Otherwise known as “batched”). If it is settled then you must do a refund.

You always want to VOID a transaction when possible because it does not create additional transaction fees, but if the transaction has been settled then you must issue a refund.

Voiding a Transaction After It Has Been Charged

You must use a transaction id from a previously charged account.

Production Request

curl -X PUT -u "login:password" -H "Accept: application/json" -H "X-PJ-Application-Key: YOUR_PRODUCTION_APP_KEY" \
     -d "status=VOID" \
    "https://api.payjunction.com/transactions/12345"

Remember that you can only VOID a transaction before it has been settled. Here is a sample error you will receive if you try to VOID a settled transaction.

{
  "errors" : [ {
    "message" : "Status can not be updated because the Transaction has been settled.",
    "parameter" : "status",
    "type" : "invalid"
  } ],
  "help" : "http://developer.payjunction.com/documentation/update-a-transaction"
}

Refunding a Previously Charged Transaction

The easiest way to issue a refund is using a previous transaction id. If you don’t specify the amount to refund then the amount will be taken for the previously charged transaction.

Warning:

In regards to doing partial refunds, using a transactionId as the payment token, it's important to know that if not explicitly stated, any other amount fields from the original charge will be refunded as well. For example, suppose we send a transaction with the following amounts included:

// Process the original charge
...
$txn_request['amountBase'] = "100.00";
$txn_request['amountTax'] = "8.50"; ... $result = process($txn_request); // $result['amountTotal'] === "108.50", $result['transactionId'] === "1234567" // Process partial refund ... $refund_request['transactionId'] = "1234567" $refund_request['amountBase'] = "50.00"; ... $result = process($refund_request) // $result['amountTotal'] === "58.50"

So why is our total refund $58.50 instead of $50.00? Because tax was included on the original transaction and since we didn't specify amountTax in the refund request, the API automatically refunded it. The way to get around this is to explicitly send a value for all amount fields to make sure that you get the exact result you were expecting. If you never refund tax/tips/surcharges/shipping you can just zero it all out:

$refund_request['amountTax'] = "0.00";
$refund_request['amountShipping'] = "0.00";
$refund_request['amountSurcharge'] = "0.00";
$refund_request['amountTip'] = "0.00";

Otherwise you'll have to do some logic to set this:

// PHP Example
function setRefundAmounts($amountArray, &$req) {
    $req['amountBase'] = isset($amountArray['amountBase']) ?
        $amountArray['amountBase'] : "0.00";
    $req['amountShipping'] = isset($amountArray['amountShipping']) ? 
        $amountArray['amountShipping'] : "0.00";
    $req['amountSurcharge'] = isset($amountArray['amountSurcharge']) ? 
        $amountArray['amountSurcharge'] : "0.00";
    $req['amountTip'] = isset($amountArray['amountTip']) ? 
        $amountArray['amountTip']: "0.00";
    $req['amountTax'] = isset($amountArray['amountTax']) ? 
        $amountArray['amountTax'] : "0.00";
}
$request = Array("transactionId" => "1234567");
setRefundAmounts(Array("amountBase" => "50.00", "amountTax" => "4.25"), $request);

// NodeJS Example
function setRefundAmounts(amountArray, req) {
    req['amountBase'] = amountArray['amountBase'] ?
        amountArray['amountBase'] : "0.00";
    req['amountShipping'] = amountArray['amountShipping'] ?
        amountArray['amountShipping'] : "0.00";
    req['amountSurcharge'] = amountArray['amountSurcharge'] ? 
        amountArray['amountSurcharge'] : "0.00";
    req['amountTip'] = amountArray['amountTip'] ? 
        amountArray['amountTip'] : "0.00";
    
    return req;
}
let request = setRefundAmounts({ "amountBase": "50.00", "amountTax": "4.25" }, { "transactionId": "1234567" });

Production Request

curl -X POST -u "login:password" -H "Accept: application/json" -H "X-PJ-Application-Key: YOUR_PRODUCTION_APP_KEY" \
     -d "action=REFUND" \
    -d "transactionId=12345" \
    "https://api.payjunction.com/transactions"

Sandbox Request

curl -X POST -u "pj-ql-01:pj-ql-01p" -H "Accept: application/json" -H "X-PJ-Application-Key: YOUR_LABS_APP_KEY" \
     -d "action=REFUND" \
    -d "transactionId=3595" \
    "https://api.payjunctionlabs.com/transactions"

Refunding a Card

This is an example of refunding a credit card without using the previous transaction id. Notice that the full card number must be provided.

Production Request

curl -X POST -u "login:password" -H "Accept: application/json" -H "X-PJ-Application-Key: YOUR_PRODUCTION_APP_KEY" \
     -d "action=REFUND" \
    -d "cardNumber=4444333322221111" \
    -d "cardExpMonth=01" \
    -d "cardExpYear=2020" \
    -d "amountBase=1.00" \
    "https://api.payjunction.com/transactions"

Sandbox Request

curl -X POST -u "pj-ql-01:pj-ql-01p" -H "Accept: application/json" -H "X-PJ-Application-Key: YOUR_LABS_APP_KEY" \
     -d "action=REFUND" \
    -d "cardNumber=4444333322221111" \
    -d "cardExpMonth=01" \
    -d "cardExpYear=2020" \
    -d "amountBase=1.00" \
    "https://api.payjunctionlabs.com/transactions"