Managing Payment Methods
Collect cards with createSetupIntent() and Stripe.js, then list, add, delete, and set the default payment method on any Shopkeeper billable model.
Shopkeeper provides methods to store, retrieve, update, and delete your customers' payment methods. The approach differs slightly depending on whether the payment method is for a subscription or a one-off charge.
Payment methods for subscriptions
When storing a payment method for future subscription use, you need to use Stripe's Setup Intents API. A Setup Intent securely collects payment details without making an immediate charge.
Create a Setup Intent and pass it to your frontend:
router.get('/payment-method', async ({ auth, view }) => {
const user = auth.getUserOrFail()
return view.render('pages/payment-method', {
intent: await user.createSetupIntent(),
})
})In your frontend, use Stripe.js to mount a card element and confirm the setup:
<input id="card-holder-name" type="text" />
<div id="card-element"></div>
<button id="card-button" data-secret="{{ intent.client_secret }}">Save Payment Method</button>
<script src="https://js.stripe.com/v3/"></script>
<script>
const stripe = Stripe('your-stripe-public-key')
const elements = stripe.elements()
const cardElement = elements.create('card')
cardElement.mount('#card-element')
const cardButton = document.getElementById('card-button')
cardButton.addEventListener('click', async () => {
const { setupIntent, error } = await stripe.confirmCardSetup(cardButton.dataset.secret, {
payment_method: {
card: cardElement,
billing_details: {
name: document.getElementById('card-holder-name').value,
},
},
})
if (error) {
// Show error.message to the user
} else {
// Send setupIntent.payment_method to your server
}
})
</script>Once you have the payment method ID from the frontend, you can add it to the customer or use it to create a subscription.
For more details, see Stripe's Setup Intents documentation.
Payment methods for single charges
For one-off charges, you don't need a Setup Intent. Instead, use Stripe.js to create a payment method directly:
<input id="card-holder-name" type="text" />
<div id="card-element"></div>
<button id="card-button">Pay</button>
<script src="https://js.stripe.com/v3/"></script>
<script>
const stripe = Stripe('your-stripe-public-key')
const elements = stripe.elements()
const cardElement = elements.create('card')
cardElement.mount('#card-element')
const cardButton = document.getElementById('card-button')
cardButton.addEventListener('click', async () => {
const { paymentMethod, error } = await stripe.createPaymentMethod('card', cardElement, {
billing_details: {
name: document.getElementById('card-holder-name').value,
},
})
if (error) {
// Show error.message to the user
} else {
// Send paymentMethod.id to your server to process the charge
}
})
</script>Pass the paymentMethod.id to your server and use it with the charge methods.
Due to Stripe limitations, you cannot use a customer's stored default payment method for single charges. The customer must enter their payment details each time.
Retrieving payment methods
List all payment methods for a customer:
const paymentMethods = await user.paymentMethods()To filter by type (e.g., sepa_debit, card):
const paymentMethods = await user.paymentMethods('sepa_debit')To retrieve the default payment method:
const defaultMethod = await user.defaultPaymentMethod()To find a specific payment method by its Stripe ID:
const paymentMethod = await user.findPaymentMethod('pm_xxxxxxx')Checking for payment methods
// Has a default payment method?
if (await user.hasDefaultPaymentMethod()) {
// ...
}
// Has any payment method?
if (await user.hasPaymentMethod()) {
// ...
}
// Has a payment method of a specific type?
if (await user.hasPaymentMethod('sepa_debit')) {
// ...
}Adding payment methods
To attach a new payment method to a customer:
await user.addPaymentMethod('pm_xxxxxxx')Updating the default payment method
To set a new default payment method:
await user.updateDefaultPaymentMethod('pm_xxxxxxx')To sync the default payment method from Stripe (useful if it was changed outside your application):
await user.updateDefaultPaymentMethodFromStripe()The default payment method is used for invoices and new subscriptions. Due to Stripe limitations, it cannot be used for single charges.
Deleting payment methods
Delete a specific payment method:
// From the PaymentMethod instance
const paymentMethod = await user.findPaymentMethod('pm_xxxxxxx')
await paymentMethod.delete()
// Or by ID
await user.deletePaymentMethod('pm_xxxxxxx')Delete all payment methods:
await user.deletePaymentMethods()To delete only payment methods of a specific type:
await user.deletePaymentMethods('sepa_debit')If a customer has an active subscription, do not allow them to delete their default payment method.
Going further
- Accepting Payments: Checkout-based payment flows
- Single Charges: One-off charges and payment intents
- Stripe Payment Methods documentation: Stripe's own guide to payment methods
Managing Customers
Create and sync Stripe customers from your AdonisJS models, adjust customer balances, manage tax IDs, and send users to the Stripe billing portal.
Taxes
Enable Stripe Tax with the calculateTaxes config flag, or define taxRates() and priceTaxRates() on your billable model, and check tax exemption status.