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

On this page