Single Charges
Beyond subscriptions, Shopkeeper lets you make one-off charges, create invoices for individual items, and issue refunds. This guide covers all the ways to charge customers outside of a subscription.
Simple charge
To make a one-off charge against a customer's payment method:
router.post('/purchase', async ({ auth, request }) => {
const user = auth.getUserOrFail()
const paymentMethodId = request.input('paymentMethodId')
const payment = await user.charge(100, paymentMethodId)
})The charge method accepts amounts in the smallest currency unit. For example, $10.00 USD is
1000 (cents).
You can pass additional Stripe charge options as a third argument:
await user.charge(100, paymentMethodId, {
description: 'One-time setup fee',
metadata: { order_id: '12345' },
})The charge method returns a Shopkeeper Payment instance on success, or throws an exception if the charge fails:
try {
const payment = await user.charge(100, paymentMethodId)
} catch (error) {
// Handle the failed charge
}You can also charge without an existing customer by using a new model instance:
const payment = await new User().charge(100, paymentMethodId)Charge with invoice
To generate an invoice for a one-off charge, use the invoice builder. This charges the customer's default payment method and creates a downloadable invoice:
// Invoice for 5 t-shirts
await user.invoice().addPrice('price_tshirt', 5).charge()You can customize the invoice with the builder's fluent API:
await user
.invoice()
.addPrice('price_tshirt', 5)
.addPrice('price_mug', 2)
.description('Order #12345')
.charge()For ad-hoc amounts without a pre-defined Stripe price, use addItem:
await user.invoice().addItem('One Time Fee', 500).charge()Using addPrice with pre-defined prices is recommended over addItem, as it gives you better
analytics in the Stripe dashboard.
The invoice builder also supports draft() to keep invoices as drafts, send() to email invoices to the customer, daysUntilDue(), withMetadata(), and currency().
Payment intents
For more control over the payment flow, you can create a payment intent directly:
router.post('/pay', async ({ auth, request }) => {
const user = auth.getUserOrFail()
const payment = await user.pay(request.input('amount'))
return payment.clientSecret()
})The client secret is returned to your frontend, where the customer completes the payment using Stripe.js.
To restrict which payment methods are available:
const payment = await user.payWith(1000, ['card', 'bancontact'])
return payment.clientSecret()The pay and payWith methods accept amounts in the smallest currency unit (e.g., cents for
USD).
Refunds
To refund a charge, use the refund method with the payment intent ID:
const payment = await user.charge(1000, paymentMethodId)
// Full refund
await user.refund(payment.id)Going further
- Accepting Payments: Checkout-based payment flows
- Managing Payment Methods: Storing and retrieving payment methods
- Invoices & Receipts: Listing and displaying invoices
- Stripe Payment Intents documentation: Stripe's guide to payment intents