Recover failed Shopify Subscriptions payments
Shopify subscription contracts are billed by attempt. When an attempt fails, the contract stays alive but the order is not created — that window is where recovery happens, and it usually ends with the customer updating their payment method in their account.
The Shopify Subscriptions events recovery depends on
- subscription_billing_attempts/failure
- A scheduled subscription charge failed — open a recovery case.
- subscription_billing_attempts/success
- The charge cleared. Close the case.
- subscription_contracts/update
- The customer changed the contract or its payment method.
Verifying Shopify Subscriptions webhooks
- Scheme
- HMAC-SHA256 over the raw body, base64-encoded
- Signature location
- X-Shopify-Hmac-Sha256
Verify before parsing. Shopify also sends X-Shopify-Shop-Domain, which you should map to the right workspace in a multi-store setup.
import { createHmac, timingSafeEqual } from "crypto";
const digest = createHmac("sha256", process.env.SHOPIFY_WEBHOOK_SECRET!)
.update(rawBody, "utf8")
.digest("base64");
const hmac = req.headers["x-shopify-hmac-sha256"] as string;
if (!timingSafeEqual(Buffer.from(digest), Buffer.from(hmac))) {
return new Response("Invalid signature", { status: 401 });
}
// topic: subscription_billing_attempts/failure
const attempt = JSON.parse(rawBody);
await rrlabs.enqueueRecovery({
provider: "shopify",
externalId: attempt.admin_graphql_api_id,
declineCode: attempt.error_code, // e.g. "expired_payment_method"
});Decline playbook for Shopify Subscriptions
| Failure signal | Recovery action |
|---|---|
| expired_payment_method | Send the customer-account payment update link. Retrying the same method will not succeed. |
| insufficient_funds | Reschedule the billing attempt a few days out and send a light reminder. |
| payment_method_not_found | The stored method was removed — ask the customer to add one before the next contract cycle. |
What happens after a payment fails
T+0s
Payment failed
Provider webhook received and verified
T+2s
AI analysis
Decline reason classified, amount at risk scored
T+5s
Dynamic email
Copy generated for that decline code and customer
Day 2
Smart retry
Retry scheduled only when the decline code is retryable
Day 2
WhatsApp push
Second channel used when the email went unanswered
On success
Recovered
Counted only when the provider confirms the charge
Timings describe the configured workflow, not a guaranteed outcome. Every step is skipped when the decline reason makes it counter-productive.
Frequently asked questions
- Does a failed billing attempt cancel the Shopify contract?
- Not immediately. The contract stays active while attempts are retried, which is the window recovery messaging targets.
- Can RRLabs retry the Shopify billing attempt?
- Retries are scheduled through Shopify's subscription APIs by the app that owns the contract. RRLabs decides when a retry makes sense and handles the customer communication.
Connect Shopify Subscriptions with read-only credentials
RRLabs reads failure events and orchestrates recovery. It never holds card data and never becomes the merchant of record.
Get early access