Payment Methods
Storing Payment Methods
In order to create subscriptions or perform "one-off" charges with Stripe, you will need to store a payment method and retrieve its identifier from Stripe. The approach used to accomplish this differs based on whether you plan to use the payment method for subscriptions or single charges, so we will examine both below.
Payment Methods for Subscriptions
When storing a customer's credit card information for future use by a subscription, the Stripe "Setup Intents" API must be used to securely gather the customer's payment method details. A "Setup Intent" indicates to Stripe the intention to charge a customer's payment method. Shopkeeper's Billable
trait includes the createSetupIntent
method to easily create a new Setup Intent. You should invoke this method from the route or controller that will render the form which gathers your customer's payment method details:
router.get('/update-payment-method', ({ auth, view }) => {
const user = auth.getUserOrFail()
return view.render('pages/update-payment-method', {
intent: await user.createSetupIntent(),
})
})
After you have created the Setup Intent and passed it to the view, you should attach its secret to the element that will gather the payment method. For example, consider this "update payment method" form:
<input id="card-holder-name" type="text">
<!-- Stripe Elements Placeholder -->
<div id="card-element"></div>
<button id="card-button" data-secret="{{ $intent->client_secret }}">
Update Payment Method
</button>
Next, the Stripe.js library may be used to attach a Stripe Element to the form and securely gather the customer's payment details:
<script src="https://js.stripe.com/v3/"></script>
<script>
const stripe = Stripe('stripe-public-key');
const elements = stripe.elements();
const cardElement = elements.create('card');
cardElement.mount('#card-element');
</script>
Next, the card can be verified and a secure "payment method identifier" can be retrieved from Stripe using Stripe's confirmCardSetup
method:
const cardHolderName = document.getElementById('card-holder-name');
const cardButton = document.getElementById('card-button');
const clientSecret = cardButton.dataset.secret;
cardButton.addEventListener('click', async (e) => {
const { setupIntent, error } = await stripe.confirmCardSetup(
clientSecret, {
payment_method: {
card: cardElement,
billing_details: { name: cardHolderName.value }
}
}
);
if (error) {
// Display "error.message" to the user...
} else {
// The card has been verified successfully...
}
});
After the card has been verified by Stripe, you may pass the resulting setupIntent.payment_method
identifier to your Adonis application, where it can be attached to the customer. The payment method can either be added as a new payment method or used to update the default payment method. You can also immediately use the payment method identifier to create a new subscription.
If you would like more information about Setup Intents and gathering customer payment details please review this overview provided by Stripe.
Payment Methods for Single Charges
Of course, when making a single charge against a customer's payment method, we will only need to use a payment method identifier once. Due to Stripe limitations, you may not use the stored default payment method of a customer for single charges. You must allow the customer to enter their payment method details using the Stripe.js library. For example, consider the following form:
<input id="card-holder-name" type="text">
<!-- Stripe Elements Placeholder -->
<div id="card-element"></div>
<button id="card-button">
Process Payment
</button>
After defining such a form, the Stripe.js library may be used to attach a Stripe Element to the form and securely gather the customer's payment details:
<script src="https://js.stripe.com/v3/"></script>
<script>
const stripe = Stripe('stripe-public-key');
const elements = stripe.elements();
const cardElement = elements.create('card');
cardElement.mount('#card-element');
</script>
Next, the card can be verified and a secure "payment method identifier" can be retrieved from Stripe using Stripe's createPaymentMethod
method:
const cardHolderName = document.getElementById('card-holder-name');
const cardButton = document.getElementById('card-button');
cardButton.addEventListener('click', async (e) => {
const { paymentMethod, error } = await stripe.createPaymentMethod(
'card', cardElement, {
billing_details: { name: cardHolderName.value }
}
);
if (error) {
// Display "error.message" to the user...
} else {
// The card has been verified successfully...
}
});
If the card is verified successfully, you may pass the paymentMethod.id
to your Adonis application and process a single charge.
Retrieving Payment Methods
The paymentMethods
method on the billable model instance returns a list of Shopkeeper's PaymentMethod
instances:
const paymentMethods = await user.paymentMethods()
By default, this method will return payment methods of every type. To retrieve payment methods of a specific type, you may pass the type
as an argument to the method:
const paymentMethods = await user.paymentMethods('sepa_debit')
To retrieve the customer's default payment method, the defaultPaymentMethod
method may be used:
const paymentMethods = await user.defaultPaymentMethod()
You can retrieve a specific payment method that is attached to the billable model using the findPaymentMethod
method:
const paymentMethod = user.findPaymentMethod(paymentMethodId)
Payment Method Presence
To determine if a billable model has a default payment method attached to their account, invoke the hasDefaultPaymentMethod
method:
if (user.hasDefaultPaymentMethod()) {
// ...
}
You may use the hasPaymentMethod
method to determine if a billable model has at least one payment method attached to their account:
if (await user.hasPaymentMethod()) {
// ...
}
This method will determine if the billable model has any payment method at all. To determine if a payment method of a specific type exists for the model, you may pass the type
as an argument to the method:
if (await user.hasPaymentMethod('sepa_debut')) {
// ...
}
Updating the Default Payment Method
The updateDefaultPaymentMethod
method may be used to update a customer's default payment method information. This method accepts a Stripe payment method identifier and will assign the new payment method as the default billing payment method:
await user.updateDefaultPaymentMethod(paymentMethod)
To sync your default payment method information with the customer's default payment method information in Stripe, you may use the updateDefaultPaymentMethodFromStripe
method:
await user.updateDefaultPaymentMethodFromStripe()
The default payment method on a customer can only be used for invoicing and creating new subscriptions. Due to limitations imposed by Stripe, it may not be used for single charges.
Adding Payment Methods
To add a new payment method, you may call the addPaymentMethod
method on the billable model, passing the payment method identifier:
await user.addPaymentMethod(paymentMethod)
To learn how to retrieve payment method identifiers please review the payment method storage documentation.
Deleting Payment Methods
To delete a payment method, you may call the delete
method on the Shopkeeper's PaymentMethod
instance you wish to delete:
await paymentMethod.delete()
The deletePaymentMethod
method will delete a specific payment method from the billable model:
await user.deletePaymentMethod('pm_visa')
The deletePaymentMethods
method will delete all of the payment method information for the billable model:
await user.deletePaymentMethods()
By default, this method will delete payment methods of every type. To delete payment methods of a specific type you can pass the type
as an argument to the method:
await user.deletePaymentMethods('sepa_debit')
If a user has an active subscription, your application should not allow them to delete their default payment method.