https://github.com/mfbx9da4/stripe-sca-subscription-react
React Stripe Elements Subscription with SCA Starter
https://github.com/mfbx9da4/stripe-sca-subscription-react
Last synced: about 1 month ago
JSON representation
React Stripe Elements Subscription with SCA Starter
- Host: GitHub
- URL: https://github.com/mfbx9da4/stripe-sca-subscription-react
- Owner: mfbx9da4
- License: mit
- Created: 2019-09-24T14:40:25.000Z (over 5 years ago)
- Default Branch: master
- Last Pushed: 2023-01-05T22:55:06.000Z (over 2 years ago)
- Last Synced: 2025-03-28T13:21:16.496Z (about 2 months ago)
- Language: JavaScript
- Size: 1.71 MB
- Stars: 4
- Watchers: 2
- Forks: 5
- Open Issues: 18
-
Metadata Files:
- Readme: README.md
- License: LICENSE.md
Awesome Lists containing this project
README
# Stripe SCA Subscriptions Example
To demo how to implement stripe SCA using react and node.
> [VIEW DEMO](https://stripe-sca-subscription-react.glitch.me)
> [EDIT ON GLITCH](https://glitch.com/edit/#!/stripe-sca-subscription-react)
> [MIRROR ON GITHUB](https://github.com/mfbx9da4/stripe-sca-subscription-react)

### Aim
- A step by step guide for integrating Stripe in an SCA compliant way for subscriptions.
- Stripe React Elements github repository also has some [demo code](https://github.com/stripe/react-stripe-elements/tree/master/demo/intents)
which I made into a [working example here](https://glitch.com/edit/#!/stripe-react-elements-express).
- [I made a live demo on glitch.com with working frontend and backend code you can fork it here, shown below](https://glitch.com/edit/#!/stripe-sca-subscription-react).
### Overview
Prerequisites
1. Upgrade stripe
2. Upgrade stripejs (server)
3. Upgrade stripe-react-elements
4. Upgrade stripejs (web)
5. Create subscriptions in dashboardIntegrate Stripe
1. Setup the intent
2. Authenticate with 3D secure
3. Attach payment method to customer
4. Start subscriptionTODO: Include links from overview
### Prerequisites
1. Upgrade to latest version of stripe in the dashboard. `2019-09-09` or later.

2. Use the latest version of `stripe` nodejs library.
```bash
npm uninstall stripe -S ; npm install stripe -S
# or if you use yarn
yarn remove stripe ; yarn add stripe
```
3. Use latest version of `react-stripe-elements`.```bash
npm uninstall react-stripe-elements -S ; npm install react-stripe-elements -S
# or if you use yarn
yarn remove react-stripe-elements ; yarn add react-stripe-elements
```4. Use latest version of client side stripe. If you are already using stripe make sure to update it. If you are starting fresh, we'll include it later in the tutorial, so don't worry about including it now!
```html
```
5. In the dashboard, [create a subscription plan](https://stripe.com/docs/billing/subscriptions/creating) or [do it programmatically](https://stripe.com/docs/api/subscriptions).
If you have a subscription it should look a bit like this in the dashboard. Jot down the `ID` shown in the image for later.
### Activate SCA
It requires these four steps
1. Setup the intent
2. Authenticate with 3D secure
3. Attach payment method to customer
4. Start subscription----
#### 1. Setup the intent
#### 1a. Setup the intent (react)
Let's create a reusable CardElement which will always use SCA.
```js
class _SCACardElement extends React.Component {
constructor(props) {
super(props)
this.state = {
clientSecret: null,
error: null
}
// We expose this so that when the form is submitted we can
// do 3D Secure from the parent
props.getHandleCardSetupRef(this.handleCardSetup)
}async componentDidMount() {
try {
let clientSecret = await api.createSetupIntent({
payment_method_types: ['card']
})
this.setState({ clientSecret, disabled: false })
} catch (err) {
this.setState({ error: err.message })
}
}
handleCardSetup = async () => this.props.stripe.handleCardSetup(this.state.clientSecret)render() {
const { error } = this.state
return (
{error &&Setup Intent Error: {error}}
)
}
}_SCACardElement.propTypes = {
getHandleCardSetupRef: PropTypes.func.isRequired
}const Card = injectStripe(_SCACardElement)
```#### 1b. Setup the intent (server)
Re
```js
app.post('/setup_intents', async (req, res) => {
try {
const { options } = req.body
const intent = await stripe.setupIntents.create(options)
res.json(intent)
} catch (err) {
console.error('SetupIntent err', err)
res.status(500).json(errToJSON(err))
}
})
```#### 2. Authenticate with 3D secure
On submit of the form we call `handlCardSetup` to trigger the SCA modal. A successful `handleCardSetup`
will get us a `paymentMethodId`.```js
class _CardForm extends React.Component {
state = {
error: null,
disabled: true,
succeeded: false,
processing: false,
message: null
}handleSubmit = async ev => {
ev.preventDefault()
this.setState({ disabled: true, processing: true })
const paymentMethodId = await this.handleCardSetup()
// Do something with the paymentMethodId ...
}
handleCardSetup = async () => {
const payload = await this.handleCardSetupRef()
if (payload.error) {
console.log('Handle Card Setup error', payload.error)
return this.setState({
error: `Handle Card Setup failed: ${payload.error.message}`,
disabled: false
})
}
console.log('Setup intent', payload.setupIntent)
this.setState({
message: `Setup succeeded! SetupIntent is in state: ${payload.setupIntent.status}`,
error: null
})
return payload.setupIntent.payment_method
}render() {
return (
Use 4000002500003155 to test 3D secure.{' '}
More cards.
this.handleCardSetupRef = ref} />
{this.state.error &&
{this.state.error}}
{this.state.message &&{this.state.message}}{!this.state.succeeded && (
{this.state.processing ? 'Processing…' : 'Start trial'}
)}
)
}
}
```#### 3. Attach payment method to customer
Now that we have our `paymentMethodId` we can attach it to the user and start the subscription.
```js
class _CardForm extends React.Component {
state = {
error: null,
disabled: true,
succeeded: false,
processing: false,
message: null
}handleSubmit = async ev => {
ev.preventDefault()
this.setState({ disabled: true, processing: true })
const paymentMethodId = await this.handleCardSetup()
if (paymentMethodId) return this.attachPaymentMethod(paymentMethodId)
}
handleCardSetup = async () => {
const payload = await this.handleCardSetupRef()
if (payload.error) {
console.log('Handle Card Setup error', payload.error)
return this.setState({
error: `Handle Card Setup failed: ${payload.error.message}`,
disabled: false
})
}
console.log('Setup intent', payload.setupIntent)
this.setState({
message: `Setup succeeded! SetupIntent is in state: ${payload.setupIntent.status}`,
error: null
})
return payload.setupIntent.payment_method
}
attachPaymentMethod = async paymentMethod => {
// Email hard coded for demo!
const attached = await api.attachPaymentMethod({
email: '[email protected]',
paymentMethod
})
console.log('Attached', attached)
this.setState({
succeeded: true,
error: null,
message: `Subscription started!`
})
}render() {
return (
Use 4000002500003155 to test 3D secure.{' '}
More cards.
this.handleCardSetupRef = ref} />
{this.state.error &&
{this.state.error}}
{this.state.message &&{this.state.message}}{!this.state.succeeded && (
{this.state.processing ? 'Processing…' : 'Start trial'}
)}
)
}
}
```#### 4. Start subscription
We can use this payment method to create the subscription after we have attached it to the user.
```js
app.post('/attach_payment_method', async (req, res) => {
try {
const { email, paymentMethod: paymentMethodId } = req.body.options// Step 1
// Get or create the stripe customer
let customerId = database.loadCustomerId(email)
if (!customerId) {
const customer = await stripe.customers.create({ email })
console.log('Created new customer', customer)
customerId = customer.id
// In production you should store the newly created customerId
// on the user model in your DB
database.storeCustomerId(email, customerId)
}// Step 2
// Attach the payment method
const paymentMethod = await stripe.paymentMethods.attach(paymentMethodId, {
customer: customerId
})
console.log('Attached Payment Method', paymentMethod)// Step 3
// Create the subscription
const subscription = await stripe.subscriptions.create({
customer: customerId,
tax_percent: 0,
...SUBSCRIPTION_PLAN
})
console.log('Created subscription', subscription)res.json({ subscription, paymentMethod })
} catch (err) {
console.error('SetupIntent err', err)
res.status(500).json(errToJSON(err))
}
})
```