Friends Of AdonisFriends Of Adonis

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.

On this page