Skip to main content

Payment Plans

A payment plan is a live subscription instance that ties a plan template to a specific customer and stored payment method. Once created, AccruPay charges the stored payment method on the renewal schedule defined by the template.


Prerequisites

Before creating a plan you need:

  1. A plan template — see Plan Templates
  2. A stored customer payment method — obtained by completing a checkout or save-a-card flow with storePaymentMethod: true

Create a plan

import AccruPay, { CURRENCY, PAYMENT_PLAN_STATUS } from '@accrupay/node';

const sdk = new AccruPay({
apiSecret: process.env.ACCRUPAY_API_SECRET!,
});

const plan = await sdk.paymentPlans.createOne({
merchantTransactionProviderId: process.env.ACCRUPAY_PROVIDER_ID,
data: {
merchantInternalPaymentPlanCode: 'plan-customer-123',
templateId: template.id,
paymentMethodId: storedPaymentMethod.id,
amount: 2999n,
currency: CURRENCY.USD,
customer: {
merchantInternalCustomerCode: 'customer-123',
},
// Override template intervals if needed:
renewalIntervalMonths: 1,
endsAfterMonths: 12,
trialPeriodDays: 14,
},
});

console.log(plan.id, plan.status);
note

templateId and paymentMethodId are both required. The plan will be rejected if either references an invalid or inactive record.


Plan lifecycle

INITIALIZING


ACTIVE ──────────────────────────────────────────────► ENDED
│ (endsAfterX reached)
│──────────────────────────────────────────────────► CANCELED
│ (cancelOne() called)
│──────────────────────────────────────────────────► INACTIVE
│ (provider paused)
└──────────────────────────────────────────────────► ERROR
(provider failure)
StatusDescription
INITIALIZINGPlan is being set up on the provider
ACTIVEPlan is running — renewals will be charged automatically
INACTIVEPlan is paused by the provider but not canceled
CANCELEDPlan was explicitly canceled via cancelOne()
ENDEDPlan reached its natural end date (endsAfterX condition met)
ERRORProvider encountered an unrecoverable error

Cancel a plan

await sdk.paymentPlans.cancelOne({
merchantPaymentPlanId: plan.id,
merchantTransactionProviderId: process.env.ACCRUPAY_PROVIDER_ID,
});

Cancellation stops future renewals. Any already-charged periods are not refunded automatically.


Query plans

List plans

getMany() supports rich filtering:

const plans = await sdk.paymentPlans.getMany({
// Filter options (all optional)
merchantInternalCustomerCode: 'customer-123',
templateId: template.id,
currency: CURRENCY.USD,
status: PAYMENT_PLAN_STATUS.ACTIVE,
take: 20,
skip: 0,
});

Fetch a single plan

const plan = await sdk.paymentPlans.getOne({
merchantPaymentPlanId: plan.id,
});

Sync a plan

Pull the latest plan state from the provider. Use this after a webhook or to confirm a cancellation took effect:

const synced = await sdk.paymentPlans.syncOne({
merchantTransactionProviderId: process.env.ACCRUPAY_PROVIDER_ID,
providerCode: plan.providerCode,
});

Plan field reference

Key fields on the plan object returned by the API:

FieldTypeDescription
statusPAYMENT_PLAN_STATUSCurrent lifecycle status
currentPeriodStartstring (ISO 8601)Start of the current billing period
currentPeriodEndstring (ISO 8601)End of the current billing period
startedAtstring (ISO 8601)When the plan became active
endsAtstring | null (ISO 8601)Scheduled end date if endsAfterX was set
trialEndsAtstring | null (ISO 8601)When the trial period ends, if applicable
canceledAtstring | null (ISO 8601)When the plan was canceled
periodCountnumberNumber of completed billing cycles

Next steps