Payload's Billing Schedule feature allows you to easily create and manage customer subscriptions.
If you don't already have at least one dedicated Processing Account for your business, you'll need to set one up, otherwise jump to Setup a New Subscription.
Add a Processing Account
<script src="https://payload.co/Payload.js"
pl-client-key="generated_client_key"
pl-btn-open="processing-account">
</script>
<script>
var pl = Payload('generated_client_token'); // See UI Authentication on how to obtain a client token
new pl.ProcessingAccount()
.on('account_created', function(evt) {
console.log(evt.account)
})
</script>
Response
{}
Every payment needs to be associated with an active Processing Account to process. Processing Accounts specify the pricing and settings for how payments should be processed, including what funding account to transmit the payment to.
Processing Accounts can be added through the dashboard or the full setup process can be integrated into your platform using the Integrated Processing Account Setup toolkit.
To integrate the processing account setup into your app or platform, you can use the Processing Account UI toolkit to capture the details for a new processing account.
Below is an example of the Processing Account form. Click the button to view.
Create a New Customer
curl "https://api.payload.co/customers/" \
-u secret_key_3bW9JMZtPVDOfFNzwRdfE: \
-d email="[email protected]" \
-d full_name="Matt Perez" \
-d primary_processing_id="processing_account_id" \
-d phone_number="1234567890"
account = pl.Customer.create(
full_name='Full name',
email='[email protected]',
phone_number='(111) 111-1111',
attrs={
"custom_attribute": "Custom input"
},
primary_processing_id='processing_account_id'
)
const customer = await pl.Customer.create({
full_name: 'Full name',
email: '[email protected]',
phone_number: '(111) 111-1111',
attrs: {
"custom_attribute": "Custom input"
},
primary_processing_id: 'processing_account_id'
})
Response
{}
When a user signs up, create a new Customer using one of Payload's SDKs.
Add Payment Methods
const card = await pl.PaymentMethod.create({
type: card,
card: {
card_number: "numeric",
expiry: "mm/yy"
},
account_id: customer_id
})
card = pl.PaymentMethod.create(
type='card',
card={
'card_number': 'numeric',
'expiry': 'mm/yy'
},
account_id='customer_id'
)
curl https://api.payload.co/payment_methods \
--header "Content-Type: application/json" \
-u generated_client_token: \
-d '{"type":"card","card":{"card_number":"numeric", "expiry":"mm/yy"},"account_id":"customer_id"}'
Secure Input Form Example (Bootstrap 4)
<form id="checkout-form" class="container" pl-form="payment">
<input type="hidden" pl-input="amount" value="10.00">
<div class="row pt-2">
<div class="form-group col-7 px-1">
<label>Card Number</label>
<div class="form-control" pl-input="card_number"></div>
</div>
<div class="form-group col-3 px-1">
<label>Expiration</label>
<div class="form-control" pl-input="expiry"></div>
</div>
<div class="form-group col-2 px-1">
<label>CVC</label>
<div class="form-control" pl-input="cvc"></div>
</div>
</div>
<div class="row pt-2">
<button class="btn btn-primary" type="submit">Pay Now</button>
</div>
</form>
Import Payload
<script src="https://payload.co/Payload.js"></script>
Initialize the Form
<script>
var pl = Payload('generated_client_token')
var checkout_form = new pl.Form({
form: $('#checkout-form').get(0)
})
</script>
Response
{}
Once the Customer account has been created, you can add Payment Methods programmatically, or use Payload's Secure Input feature to securely capture card or bank account information from any HTML form.
Create Billing Schedule
curl "https://api.payload.co/billing_schedules/" \
-u secret_key_3bW9JMZtPVDOfFNzwRdfE: \
-d start_date="2019-01-01" \
-d end_date="2019-12-31" \
-d recurring_frequency="monthly" \
-d type="subscription" \
-d processing_id="acct_3bW9JMapnT7sw7neax7ui" \
-d charges[0][type]="supreme" \
-d charges[0][amount]=19.99 \
-d customer_id="acct_3bW9JMorTV1q6mXkkXUTg"
billing_schedule = pl.BillingSchedule.create(
start_date=datetime.date(2019, 1, 1),
end_date=datetime.date(2019, 12, 31),
type='subscription',
processing_id='acct_3bW9JMapnT7sw7neax7ui',
recurring_frequency='monthly',
charges=[
pl.BillingCharge(type='supreme', amount=19.99)
],
customer_id='acct_3bW9JMorTV1q6mXkkXUTg'
)
const billing_schedule = await pl.BillingSchedule.create({
start_date: '2019-01-01',
end_date: '2019-12-31',
type: 'subscription',
processing_id: 'acct_3bW9JMapnT7sw7neax7ui',
recurring_frequency: 'monthly',
charges: [
new pl.BillingCharge({
type: 'supreme',
amount: 19.99
})
],
customer_id: 'acct_3bW9JMorTV1q6mXkkXUTg'
})
Response
{}
Now that the Customer has stored payment information, it's easy to create a customized billing schedule based on your pricing plans.
Your billing schedule will generate an Invoice every period (based on its recurring_frequency
). You can either set up automatic billing or trigger those payments manually.
Update Default Billing Method
curl "https://api.payload.co/payment_methods/pm_3bW9JMoHcb0u4ZmM7FZ32" \
-X PUT \
-u secret_key_3bW9JMZtPVDOfFNzwRdfE: \
-d default_payment_method=true
payment_method.update(
default_payment_method=True
)
customer = await customer.update({
default_payment_method: true
})
To enable automatic billing, update the default_payment_method
flag to true
on the Customer's desired payment method object. The Customer's default Payment Method will be automatically charged on the due date of any invoice that gets generated by the Billing Schedule.
Create a Webhook
curl "https://api.payload.co/webhooks/" \
-u secret_key_3bW9JMZtPVDOfFNzwRdfE: \
-d trigger="automatic_payment" \
-d url="https://api.your-app.com/webhooks-endpoint"
webhook = pl.Webhooks.create(
trigger='automatic_payment',
url='https://api.your-app.com/webhooks-endpoint'
)
const webhook = await pl.Webhooks.create({
trigger: 'payment',
url: 'https://api.your-app.com/webhooks-endpoint'
})
Response
{}
Use Payload's webhooks to watch for successful and unsuccessful subscription payments. Some useful triggers are: automatic_payment
, payment
, reject
, and decline
. For a full list of webhook triggers, see the Object Reference.
Pay an Invoice
curl -s "https://api.payload.co/transactions" \
-u secret_key_3bW9JMZtPVDOfFNzwRdfE:\
-X POST \
-d customer_id=acct_3bW9JND5iKnAlOYn9u82q \
-d amount=100 \
-d type=payment \
-d allocations[0][invoice_id]=inv_3bW9JND5iKnAlOYn9u82q
invoice = pl.Invoice.get('inv_3bW9JND5iKnAlOYn9u82q')
if invoice.status != 'paid':
payment = pl.Payment.create(
amount=invoice.amount_due,
customer_id=invoice.customer_id,
allocations=[
pl.PaymentItem( invoice_id=invoice.id )
]
))
const invoice = await pl.Invoice.get('inv_3bW9JND5iKnAlOYn9u82q')
if (invoice.status !== 'paid') {
const payment = await pl.Payment.create({
amount: invoice.amount_due,
customer_id: invoice.customer_id,
allocations: [
new pl.PaymentItem({
invoice_id: invoice.id,
})
]
})
}
Invoices can be accessed through the Billing Schedule object's invoices
field, or at the API endpoints https://api.payload.co/billing_schedules/{id}/invoices
or https://api.payload.co/invoices/{id}
.
Once you've found the unpaid Invoice, pay it by creating a payment object with a nested payment line item within the allocations nested array. This will allocate the transaction to the invoice, subtracting from any balance due on that invoice.
Thanks to Payload's intuitive, object-oriented design, it's easy to manage subscriptions using update
operations on the Billing Schedule and Billing Charge objects.
Update Billing Schedule End Date
curl "https://api.payload.co/billing_schedule/{id}" \
-X PUT \
-u secret_key_3bW9JMZtPVDOfFNzwRdfE: \
-d end_date="yyyy-mm-dd"
billing_schedule.update(
end_date='yyyy-mm-dd'
)
const update = await billing_schedule.update({ end_date: 'yyyy-mm-dd' })
Response
{}
To cancel a subscription, simply update the Billing Schedule's end_date
. After the end_date
is reached, no additional Invoices will be generated.
Update a Billing Charge
curl "https://api.payload.co/billing_charge/{id}" \
-X PUT \
-u secret_key_3bW9JMZtPVDOfFNzwRdfE: \
-d amount="19.99" \
-d type="supreme"
billing_charge.update(
type='supreme',
amount=19.99
)
const update = await billing_charge.update({
type: 'supreme',
amount: 19.99
})
Response
{}
To upgrade a subscription, update
the appropriate Billing Charge object in the charges
array.
Query your Billing Schedules, Payments, Customers, and any other object associated with your subscriptions using our powerful query language with built-in aggregate response value modifier functions and group by capabilities.
With our software libraries' ARM (API Relational Model) design, constructing complex reporting queries is simple and intuitive.
results = pl.Invoice.select(
pl.attr.amount_due.sum()
pl.attr.status,
pl.attr.type
).filter_by(
pl.attr.status == 'overdue'
).group_by(
pl.attr.customer_id
).all()
const results = await pl.Invoice
.select(
pl.Invoice.amount_due.sum(),
pl.Invoice.status,
pl.Invoice.customer_id
)
.filterBy(pl.Invoice.status.eq('overdue'))
.groupBy(pl.Invoice.customer_id)
customer_id | status | sum(amount_due) |
---|---|---|
acct_3btsXsJQrwdf6FD0FnWU7 | overdue | 59.94 |
acct_3btxqcWaQOhSb8jRk3cm7 | overdue | 29.97 |
acct_3bvLzBP92YQhkRjusD195 | overdue | 119.88 |
acct_3bvM06IcMoIklRmC8yfmy | overdue | 89.91 |
results = pl.BillingCharge.select(
pl.attr.id.count(),
pl.attr.type,
pl.attr.created_at.year()
).filter_by(
pl.attr.created_at.year() == 2019
).group_by(
pl.attr.type
).all()
const results = await pl.BillingCharge
.select(
pl.BillingCharge.id.count(),
pl.BillingCharge.type,
pl.BillingCharge.created_at.year()
)
.filterBy(pl.BillingCharge.created_at.year().eq(2019))
.groupBy(pl.BillingCharge.type)
count(id) | type | year(created_at) |
---|---|---|
1244 | basic | 2019 |
687 | premium | 2019 |
322 | supreme | 2019 |