How It Works
When a payment completes, your backend callspaymentRecords.reportPayment() and embeds the HitPay payment ID inside the Payment Record — both in processor_details and in metadata. This creates a persistent, queryable link between the Stripe record and the HitPay payment that you can use to issue refunds later.
Key Concepts
| Component | Purpose |
|---|---|
| Stripe Payment Record | The source of truth linking Stripe and HitPay. Stores the HitPay payment ID in processor_details.custom.payment_reference and metadata.hitpay_payment_id. |
| HitPay Refund API | Executes the actual money movement. This is the critical step — if it fails, the refund is aborted. |
paymentRecords.reportRefund() | Reports the completed HitPay refund back to Stripe so it appears in unified reporting and reconciliation. |
API References
HitPay Refund API
Initiate a refund by
payment_id and amount.Stripe reportRefund
Report a completed refund back to a Stripe Payment Record.
Stripe Payment Records
Retrieve Payment Records and their metadata.
How the HitPay Payment ID is Stored
At payment confirmation time, your backend callspaymentRecords.reportPayment() with the HitPay payment ID embedded in two fields:
Storing HitPay payment ID in reportPayment()
Storing HitPay payment ID in reportPayment()
The Payment Record ID itself is typically stored on the PaymentIntent metadata as
stripe_payment_record_id at the time of payment confirmation, so you can always trace: PaymentIntent → Payment Record → HitPay Payment.Refund Flow
The HitPay refund call is the only step that aborts the flow on failure. The Stripe
reportRefund call is best-effort — if it fails, the customer has still been refunded by HitPay.API Calls Summary
| Step | System | Method / Endpoint | Purpose | Blocking? |
|---|---|---|---|---|
| 1 | Stripe | paymentRecords.retrieve(id) | Fetch the HitPay payment ID from Payment Record metadata | Yes |
| 2 | HitPay | POST /v1/refund | Execute the actual refund — moves funds back to the customer | Yes — aborts on failure |
| 3 | Stripe | paymentRecords.reportRefund(id, {...}) | Report the completed refund back to Stripe | No |
Implementation
Retrieve the Payment Record
Server-sideLook up the Stripe Payment Record to retrieve the HitPay payment ID. The Payment Record ID should have been stored on the PaymentIntent metadata as
stripe_payment_record_id at payment confirmation time.Retrieve Payment Record
Retrieve Payment Record
Call the HitPay Refund API
Server-side · Critical pathConvert the amount from cents to a decimal string and call HitPay’s refund endpoint. If this fails, the entire refund is aborted.
Call HitPay Refund API
Call HitPay Refund API
Subscription Invoices
For subscriptions, payments are tied to Stripe invoices rather than PaymentIntents directly. The same linkage applies — when the HitPay payment is confirmed for a subscription invoice, store both IDs in the invoice metadata:Error Handling
The refund flow uses a critical path / best-effort pattern:| Step | Failure Behavior |
|---|---|
HitPay POST /v1/refund | Returns error immediately — Stripe reportRefund does not execute |
paymentRecords.reportRefund | Logs error and continues — customer has already been refunded |
Testing
- Complete a test payment using a sandbox HitPay payment method
- Confirm the Payment Record has
hitpay_payment_idin its metadata - Initiate a refund with an amount ≤ the original payment
- Verify in the HitPay dashboard: refund appears under the original payment
- Verify in the Stripe dashboard: Payment Record shows the refund report
| System | Environment Variable |
|---|---|
| HitPay | HITPAY_API_KEY_SANDBOX |
| Stripe | STRIPE_SECRET_KEY_SANDBOX |
Troubleshooting
'Payment Record is missing the HitPay payment ID'
'Payment Record is missing the HitPay payment ID'
The
hitpay_payment_id was not written to the Payment Record’s metadata when the original payment was confirmed. Check your paymentRecords.reportPayment() call and ensure the metadata object includes hitpay_payment_id. See One-Time Payments.HitPay refund API returns an error
HitPay refund API returns an error
Common causes:
- Invalid
payment_id: Ensure you’re passing the HitPay payment ID, not a Stripe ID. - Amount exceeds original payment: HitPay rejects amounts greater than what was paid.
- Already refunded: HitPay does not allow a second refund on a fully refunded payment.
- Wrong API key or environment: Confirm your
HITPAY_API_KEYmatches the environment (sandbox vs. production).
Stripe reportRefund fails with 'Payment Record not found'
Stripe reportRefund fails with 'Payment Record not found'
The Payment Record ID may be from a different Stripe account or environment. Confirm the
STRIPE_SECRET_KEY matches the account where the Payment Record was originally created.