Before delivering a product (such as your podcast or your article), you must verify that the payment was indeed successful. You can do this on the server by validating the signature of the payment response.

Note: Payment verification is highly recommended, but not mandatory. Failing to verify payments allow savvy users to create "fake" payments, tricking you into delivering your product. If you think your payment scenario does not require payment verification (e.g., donation buttons), you can skip this step.

Verification steps

To validate a payment response, you will need the SHA256 hash of your API secret.

You can compute this in NodeJS with:

const hashedSecret = crypto.createHash("SHA256").update(QUID_API_SECRET).digest('base64');

This hash can be used to calculate a message authentication code (HMAC) for the payment response. If the HMAC matches the signature on the response, then the payment was valid and successful.

The signature can be calculated by concatenating the id , userHash , merchantID , productID , currency , amount , and tsUnix fields of the payment response with a comma separator, calculating it's SHA256 HMAC, and serializing it into a Base 64 sting.

Javascript Example

Example code for a Javascript backend (processing a POST callback):

const crypto = require('crypto');

// Hash API secret for receipt verification
var secret = crypto.createHash("SHA256").update(QUID_API_SECRET).digest('base64');

app.post('/buyArticle',function(request, response){
  // Signed payload
  const body = request.body;
  const payload = [
    body.id,
    body.userHash,
    body.merchantID,
    body.productID,
    body.currency,
    body.amount,
    body.tsUnix
  ].join(",");
 
  // Calculate signature of payload using secret
  const sig = crypto.createHmac('SHA256', secret).update(payload).digest('base64');

  // Check if sig matches receipt signature
  if (sig == request.body.sig) {
    // Receipt verified, deliver content
    returnArticle(response, articleID);
  } else {
    response.status(404).send('receipt verification failed');
  }
});

Go Example

// VerifyReceipt returns true if the payment response has a valid signature
func VerifyReceipt(pr *PaymentResponse) bool {
payload := strings.Join([]string{
pr.ID,
pr.UserHash,
pr.MerchantID,
pr.ProductID,
pr.Currency,
pr.Amount,
fmt.Sprint(pr.TSUnix),
}, ",")

secretHash := sha256.New()
secretHash.Write(QUID_API_SECRET)
secret := base64.URLEncoding.EncodeToString(secretHash.Sum(nil))
   
hash := hmac.New(sha256.New, []byte(secret))
hash.Write(payload)
sig := base64.StdEncoding.EncodeToString(hash.Sum(nil))
return (sig == pr.Sig)
}

PHP Example

function VerifyReceipt($paymentResponse) {
    $payloadArray = [
        $paymentResponse->id,
        $paymentResponse->userHash,
        $paymentResponse->merchantID,
        $paymentResponse->productID,
        $paymentResponse->currency,
        $paymentResponse->amount,
        $paymentResponse->tsUnix,
    ];
    $payload = implode(',', $payloadArray);

    $secret = base64_encode(hash('sha256', $QUID_API_SECRET, true));

    $sig = base64_encode(hash_hmac('sha256', $payload, $secret, true));

    return ($sig == $paymentResponse->sig);
}
Did this answer your question?