{"id":21875138,"url":"https://github.com/mfbx9da4/stripe-sca-subscription-react","last_synced_at":"2025-04-15T01:25:50.909Z","repository":{"id":39369889,"uuid":"210626011","full_name":"mfbx9da4/stripe-sca-subscription-react","owner":"mfbx9da4","description":"React Stripe Elements Subscription with SCA Starter","archived":false,"fork":false,"pushed_at":"2023-01-05T22:55:06.000Z","size":1791,"stargazers_count":4,"open_issues_count":18,"forks_count":5,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-28T13:21:16.496Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mfbx9da4.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-09-24T14:40:25.000Z","updated_at":"2023-08-18T16:19:50.000Z","dependencies_parsed_at":"2023-02-04T22:02:03.356Z","dependency_job_id":null,"html_url":"https://github.com/mfbx9da4/stripe-sca-subscription-react","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mfbx9da4%2Fstripe-sca-subscription-react","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mfbx9da4%2Fstripe-sca-subscription-react/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mfbx9da4%2Fstripe-sca-subscription-react/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mfbx9da4%2Fstripe-sca-subscription-react/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mfbx9da4","download_url":"https://codeload.github.com/mfbx9da4/stripe-sca-subscription-react/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248987409,"owners_count":21194220,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-28T07:14:41.838Z","updated_at":"2025-04-15T01:25:50.891Z","avatar_url":"https://github.com/mfbx9da4.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Stripe SCA Subscriptions Example\n\nTo demo how to implement stripe SCA using react and node.\n\n\u003e [VIEW DEMO](https://stripe-sca-subscription-react.glitch.me)\n\n\u003e [EDIT ON GLITCH](https://glitch.com/edit/#!/stripe-sca-subscription-react)\n\n\u003e [MIRROR ON GITHUB](https://github.com/mfbx9da4/stripe-sca-subscription-react)\n\n![YfiWzk7Xdc](https://user-images.githubusercontent.com/1690659/65703231-58174f80-e07c-11e9-9352-8616bfdb52dc.gif)\n\n\n### Aim\n\n- A step by step guide for integrating Stripe in an SCA compliant way for subscriptions.\n- Stripe React Elements github repository also has some [demo code](https://github.com/stripe/react-stripe-elements/tree/master/demo/intents) \nwhich I made into a [working example here](https://glitch.com/edit/#!/stripe-react-elements-express).\n- [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).\n\n![edAUunAs5S](https://user-images.githubusercontent.com/1690659/65525116-e8bc2700-dee6-11e9-814f-96ece40653cd.gif)\n\n### Overview\n\nPrerequisites\n1. Upgrade stripe\n2. Upgrade stripejs (server)\n3. Upgrade stripe-react-elements\n4. Upgrade stripejs (web)\n5. Create subscriptions in dashboard\n\nIntegrate Stripe\n1. Setup the intent\n2. Authenticate with 3D secure\n3. Attach payment method to customer\n4. Start subscription\n\nTODO: Include links from overview\n\n### Prerequisites\n\n1. Upgrade to latest version of stripe in the dashboard. `2019-09-09` or later.\n\n![Upgraded stripe version](https://user-images.githubusercontent.com/1690659/65693247-e6370a00-e06b-11e9-96b5-53c9e5c129f0.png)\n\n\n2. Use the latest version of `stripe` nodejs library.\n\n```bash\nnpm uninstall stripe -S ; npm install stripe -S\n# or if you use yarn \nyarn remove stripe ; yarn add stripe \n```\n \n3. Use latest version of `react-stripe-elements`.\n\n```bash\nnpm uninstall react-stripe-elements -S ; npm install react-stripe-elements -S\n# or if you use yarn \nyarn remove react-stripe-elements ; yarn add react-stripe-elements \n```\n\n4. 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!\n\n```html\n\u003cscript src=\"https://js.stripe.com/v3/\"\u003e\u003c/script\u003e\n```\n\n5. In the dashboard, [create a subscription plan](https://stripe.com/docs/billing/subscriptions/creating) or [do it programmatically](https://stripe.com/docs/api/subscriptions). \nIf you have a subscription it should look a bit like this in the dashboard. Jot down the `ID` shown in the image for later.\n\n![dashboard subscription](https://user-images.githubusercontent.com/1690659/65694524-e9cb9080-e06d-11e9-8433-8a42407ce361.png)\n\n\n### Activate SCA\n\nIt requires these four steps\n\n1. Setup the intent\n2. Authenticate with 3D secure\n3. Attach payment method to customer\n4. Start subscription\n\n\n----\n\n#### 1. Setup the intent\n\n#### 1a. Setup the intent (react)\n\nLet's create a reusable CardElement which will always use SCA.\n```js\nclass _SCACardElement extends React.Component {\n  constructor(props) {\n    super(props)\n    this.state = {\n      clientSecret: null,\n      error: null\n    }\n    // We expose this so that when the form is submitted we can\n    // do 3D Secure from the parent \n    props.getHandleCardSetupRef(this.handleCardSetup)\n  }\n\n  async componentDidMount() {\n    try {\n      let clientSecret = await api.createSetupIntent({\n        payment_method_types: ['card']\n      })\n      this.setState({ clientSecret, disabled: false })\n    } catch (err) {\n      this.setState({ error: err.message })\n    }\n  }\n  \n  handleCardSetup = async () =\u003e this.props.stripe.handleCardSetup(this.state.clientSecret)\n\n  render() {\n    const { error } = this.state\n    return (\n        \u003cReact.Fragment\u003e\n          \u003cCardElement\n            hidePostalCode\n            onBlur={handleBlur}\n            onChange={handleChange}\n            onFocus={handleFocus}\n            onReady={handleReady}\n            {...createOptions(this.props.fontSize)}\n          /\u003e\n        {error \u0026\u0026 \u003cdiv className='error' key='SetupIntentError'\u003eSetup Intent Error: {error}\u003c/div\u003e}\n      \u003c/React.Fragment\u003e\n    )\n  }\n}\n\n_SCACardElement.propTypes = {\n  getHandleCardSetupRef: PropTypes.func.isRequired\n}\n\nconst Card = injectStripe(_SCACardElement)\n```\n\n#### 1b. Setup the intent (server)\n\nRe\n\n```js\napp.post('/setup_intents', async (req, res) =\u003e {\n  try {\n    const { options } = req.body\n    const intent = await stripe.setupIntents.create(options)\n    res.json(intent)\n  } catch (err) {\n    console.error('SetupIntent err', err)\n    res.status(500).json(errToJSON(err))\n  }\n})\n```\n\n#### 2. Authenticate with 3D secure\n\nOn submit of the form we call `handlCardSetup` to trigger the SCA modal. A successful `handleCardSetup`\nwill get us a `paymentMethodId`.\n\n```js\nclass _CardForm extends React.Component {\n  state = {\n    error: null,\n    disabled: true,\n    succeeded: false,\n    processing: false,\n    message: null\n  }\n\n  handleSubmit = async ev =\u003e {\n    ev.preventDefault()\n    this.setState({ disabled: true, processing: true })\n    const paymentMethodId = await this.handleCardSetup()\n    // Do something with the paymentMethodId ...\n  }\n  \n  handleCardSetup = async () =\u003e {\n    const payload = await this.handleCardSetupRef()\n    if (payload.error) {\n      console.log('Handle Card Setup error', payload.error)\n      return this.setState({\n        error: `Handle Card Setup failed: ${payload.error.message}`,\n        disabled: false\n      })\n    }\n    console.log('Setup intent', payload.setupIntent)\n    this.setState({\n      message: `Setup succeeded! SetupIntent is in state: ${payload.setupIntent.status}`,\n      error: null\n    })\n    return payload.setupIntent.payment_method\n  }\n\n  render() {\n    return (\n      \u003cform onSubmit={this.handleSubmit}\u003e\n        \u003clabel\u003e\n          Use 4000002500003155 to test 3D secure.{' '}\n          \u003ca href=\"https://stripe.com/docs/testing#testing\"\u003eMore cards\u003c/a\u003e.\n        \u003c/label\u003e\n\n        \u003cCard getHandleCardSetupRef={ref =\u003e this.handleCardSetupRef = ref} /\u003e\n\n        {this.state.error \u0026\u0026 \u003cdiv key='error' className=\"error\"\u003e{this.state.error}\u003c/div\u003e}\n        {this.state.message \u0026\u0026 \u003cdiv key='message' className=\"message\"\u003e{this.state.message}\u003c/div\u003e}\n\n        {!this.state.succeeded \u0026\u0026 (\n          \u003cbutton disabled={this.state.disabled}\u003e\n            {this.state.processing ? 'Processing…' : 'Start trial'}\n          \u003c/button\u003e\n        )}\n      \u003c/form\u003e\n    )\n  }\n}\n```\n\n#### 3. Attach payment method to customer\n\nNow that we have our `paymentMethodId` we can attach it to the user and start the subscription.\n\n```js\nclass _CardForm extends React.Component {\n  state = {\n    error: null,\n    disabled: true,\n    succeeded: false,\n    processing: false,\n    message: null\n  }\n\n  handleSubmit = async ev =\u003e {\n    ev.preventDefault()\n    this.setState({ disabled: true, processing: true })\n    const paymentMethodId = await this.handleCardSetup()\n    if (paymentMethodId) return this.attachPaymentMethod(paymentMethodId)\n  }\n  \n  handleCardSetup = async () =\u003e {\n    const payload = await this.handleCardSetupRef()\n    if (payload.error) {\n      console.log('Handle Card Setup error', payload.error)\n      return this.setState({\n        error: `Handle Card Setup failed: ${payload.error.message}`,\n        disabled: false\n      })\n    }\n    console.log('Setup intent', payload.setupIntent)\n    this.setState({\n      message: `Setup succeeded! SetupIntent is in state: ${payload.setupIntent.status}`,\n      error: null\n    })\n    return payload.setupIntent.payment_method\n  }\n  \n  attachPaymentMethod = async paymentMethod =\u003e {\n    // Email hard coded for demo!\n    const attached = await api.attachPaymentMethod({\n      email: 'david@crowdform.co.uk',\n      paymentMethod\n    })\n    console.log('Attached', attached)\n    this.setState({\n      succeeded: true,\n      error: null,\n      message: `Subscription started!`\n    })\n  }\n\n  render() {\n    return (\n      \u003cform onSubmit={this.handleSubmit}\u003e\n        \u003clabel\u003e\n          Use 4000002500003155 to test 3D secure.{' '}\n          \u003ca href=\"https://stripe.com/docs/testing#testing\"\u003eMore cards\u003c/a\u003e.\n        \u003c/label\u003e\n\n        \u003cCard getHandleCardSetupRef={ref =\u003e this.handleCardSetupRef = ref} /\u003e\n\n        {this.state.error \u0026\u0026 \u003cdiv key='error' className=\"error\"\u003e{this.state.error}\u003c/div\u003e}\n        {this.state.message \u0026\u0026 \u003cdiv key='message' className=\"message\"\u003e{this.state.message}\u003c/div\u003e}\n\n        {!this.state.succeeded \u0026\u0026 (\n          \u003cbutton disabled={this.state.disabled}\u003e\n            {this.state.processing ? 'Processing…' : 'Start trial'}\n          \u003c/button\u003e\n        )}\n      \u003c/form\u003e\n    )\n  }\n}\n```\n\n#### 4. Start subscription\n\nWe can use this payment method to create the subscription after we have attached it to the user. \n\n```js\napp.post('/attach_payment_method', async (req, res) =\u003e {\n  try {\n    const { email, paymentMethod: paymentMethodId } = req.body.options\n\n    // Step 1\n    // Get or create the stripe customer\n    let customerId = database.loadCustomerId(email)\n    if (!customerId) {\n      const customer = await stripe.customers.create({ email })\n      console.log('Created new customer', customer)\n      customerId = customer.id\n      // In production you should store the newly created customerId\n      // on the user model in your DB\n      database.storeCustomerId(email, customerId)\n    }\n\n    // Step 2\n    // Attach the payment method\n    const paymentMethod = await stripe.paymentMethods.attach(paymentMethodId, {\n      customer: customerId\n    })\n    console.log('Attached Payment Method', paymentMethod)\n\n    // Step 3\n    // Create the subscription\n    const subscription = await stripe.subscriptions.create({\n      customer: customerId,\n      tax_percent: 0,\n      ...SUBSCRIPTION_PLAN\n    })\n    console.log('Created subscription', subscription)\n\n    res.json({ subscription, paymentMethod })\n  } catch (err) {\n    console.error('SetupIntent err', err)\n    res.status(500).json(errToJSON(err))\n  }\n})\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmfbx9da4%2Fstripe-sca-subscription-react","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmfbx9da4%2Fstripe-sca-subscription-react","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmfbx9da4%2Fstripe-sca-subscription-react/lists"}