无法使用Stripe 3D Secure和React Native创建活动订阅(循环订阅)。

huangapple go评论93阅读模式
英文:

Unable to create active subscription (recurring) with Stripe 3D Secure, react native

问题

我无法创建一个使用React Native前端和Node后端的Stripe 3D Secure的活动订阅。订阅已经成功创建,并且对于非3D Secure的Stripe支付是活动的。但是对于3D Secure,支付已经验证并在支付仪表板上更新,但是订阅并没有被更新为活动状态。

以下是订阅(循环)的截图:
无法使用Stripe 3D Secure和React Native创建活动订阅(循环订阅)。

支付的截图:
无法使用Stripe 3D Secure和React Native创建活动订阅(循环订阅)。

我已经按照这个Stack Overflow回答的工作流程进行了操作,但仍然无法正常工作。

前端代码:

  const createSubscription = async () => {
    try {
      if (!cardFormData?.complete) {
        alert('Please input your card data!');
        setSaving(false);
        return;
      }

      const {firstName, lastName} = userData;
      const name = `${firstName} ${lastName}`;

      const billing_details = {
        email,
        name,
        description: name,
      };

      // making payment method
      const paymentMethod = await createPaymentMethod({
        paymentMethodType: 'Card',
        card: cardFormData,
        billing_details,
      });

      const body = JSON.stringify({
        paymentMethod: paymentMethod?.paymentMethod?.id,
        name,
        email,
      });

      console.log('going to make the api call');

      // call the backend to create subscription / customer / client secret
      const response = await fetch(`${APIURL}/pay/makePayment`, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body,
      });

      const respJson = await response?.json();
      const {
        clientSecret,
        error: apiError,
        customerId,
        subscriptionId,
        subscriptionPaymentMethod,
        subscriptionStart,
        subscriptionEnd,
      } = respJson;

      if (apiError) {
        alert(`Payment Error 1 ` + apiError);
        setSaving(false);
      } else {
        // confirm the payment by the user
        console.log('confirming payment');
        const {paymentIntent, error} = await confirmPayment(clientSecret, {
          type: 'Card',
          paymentMethodType: 'Card',
          paymentMethodData: {
            billing_details,
            payment_method: subscriptionPaymentMethod,
          },
        });
        console.log('clientSecret', clientSecret);
        console.log('Object', {
          type: 'Card',
          paymentMethodType: 'Card',
          paymentMethodData: {
            billing_details,
            payment_method: subscriptionPaymentMethod,
          },
        });
        console.log('paymentIntent', paymentIntent);
        if (paymentIntent) {
          createUser(
            customerId,
            subscriptionId,
            subscriptionStart,
            subscriptionEnd,
          );
        } else if (error) {
          alert(`Payment Error`);
          setSaving(false);
        }
      }
    } catch (e) {
      alert(`Payment Error 2 ` + e?.message);
      setSaving(false);
    }
  };

后端代码:

router.post("/makePayment", async (req, res) => {
  try {
    const { email, name, paymentMethod } = req.body;
    if (email === "" || name === "" || !email || !name || !paymentMethod)
      throw "";

    // making new customer
    const customer = await addNewCustomer(email, name, paymentMethod);

    // making new subscription
    const subscription = await addSubscription(customer?.id);

    // getting stripe secret
    const { error, clientSecret } = await getStripeSecret(
      subscription?.latest_invoice?.payment_intent?.payment_method,
      customer?.id
    );

    // saving data in session
    req.session.customerId = customer?.id;
    req.session.subscriptionId = subscription?.id;

    res
      .send({
        clientSecret,
        error,
        customerId: customer?.id,
        subscriptionId: subscription?.id,
        subscriptionPaymentMethod:
          subscription?.latest_invoice?.payment_intent?.payment_method,
        subscriptionStart: new Date(subscription.current_period_start * 1000),
        subscriptionEnd: new Date(subscription.current_period_end * 1000),
      })
      .status(!error ? 200 : 500);
  } catch (e) {
    res.send({ error: "Internal Server Error" }).status(500);
  }
});

后端函数:

import Stripe from "stripe";
const { STRIPE_SECRET_KEY, STRIPE_API_VERSION } = process?.env;
const stripe = Stripe(STRIPE_SECRET_KEY, { apiVersion: STRIPE_API_VERSION });

export const getStripeSecret = async (paymentMethod, customerId) => {
  try {
    const paymentIntent = await stripe.paymentIntents.create({
      amount: 28000, //lowest denomination of particular currency
      currency: "aud",
      payment_method_types: ["card"], //by default
      payment_method: paymentMethod,
      customer: customerId,
    });
    const clientSecret = paymentIntent?.client_secret;

    return { clientSecret };
  } catch (e) {
    return { error: e?.message };
  }
};

export const addNewCustomer = async (email, name, paymentMethod) => {
  try {
    const customer = await stripe.customers.create({
      email,
      description: name,
      payment_method: paymentMethod,
      invoice_settings: {
        default_payment_method: paymentMethod,
      },
    });
    return customer;
  } catch (e) {
    return { error: e?.message };
  }
};

export const addSubscription = async (customerId) => {
  try {
    const subscription = await stripe.subscriptions.create({
      customer: customerId,
      items: [{ price: process.env.STRIPE_PRICE_ID }],
      payment_settings: {
        payment_method_options: {
          card: {
            request_three_d_secure: "automatic",
          },
        },
        payment_method_types: ["card"],
        save_default_payment_method: "on_subscription",
      },
      expand: ["latest_invoice.payment_intent"],
    });
    return subscription;
  } catch (e) {
    return { error: e?.message };
  }
};

我尝试按照上面分享的代码进行操作。我希望我的订阅能够像没有3D Secure一样处于活动状态。

没有3D Secure:
无法使用Stripe 3D Secure和React Native创建活动订阅(循环订阅)。

有3D Secure:
无法使用Stripe 3D Secure和React Native创建活动订阅(循环订阅)。

英文:

I am unable to create an active subscription with Stripe 3D secure using react-native frontend, node backend. The subscription has been created fine and is active for non 3D Secure Stripe payments. For 3D secure, the payment is varified and updated on payment dashboard, but the subscription is not being updated to be active at all.

Screenshots of the subscription (recurring):
无法使用Stripe 3D Secure和React Native创建活动订阅(循环订阅)。

Screenshot of the payment:
无法使用Stripe 3D Secure和React Native创建活动订阅(循环订阅)。

I have followed the workflow of this stackoverflow answer, but it still does not work.

Frontend Code:

  const createSubscription = async () => {
    try {
      if (!cardFormData?.complete) {
        alert('Please input your card data!');
        setSaving(false);
        return;
      }

      const {firstName, lastName} = userData;
      const name = `${firstName} ${lastName}`;

      const billing_details = {
        email,
        name,
        description: name,
      };

      // making payment method
      const paymentMethod = await createPaymentMethod({
        paymentMethodType: 'Card',
        card: cardFormData,
        billing_details,
      });

      const body = JSON.stringify({
        paymentMethod: paymentMethod?.paymentMethod?.id,
        name,
        email,
      });

      console.log('going to make the api call');

      // call the backend to create subscription / customer / client secret
      const response = await fetch(`${APIURL}/pay/makePayment`, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body,
      });

      const respJson = await response?.json();
      const {
        clientSecret,
        error: apiError,
        customerId,
        subscriptionId,
        subscriptionPaymentMethod,
        subscriptionStart,
        subscriptionEnd,
      } = respJson;

      if (apiError) {
        alert(`Payment Error 1 ` + apiError);
        setSaving(false);
      } else {
        // confirm the payment by the user
        console.log('confirming payment');
        const {paymentIntent, error} = await confirmPayment(clientSecret, {
          type: 'Card',
          paymentMethodType: 'Card',
          paymentMethodData: {
            billing_details,
            payment_method: subscriptionPaymentMethod,
          },
        });
        console.log('clientSecret', clientSecret);
        console.log('Object', {
          type: 'Card',
          paymentMethodType: 'Card',
          paymentMethodData: {
            billing_details,
            payment_method: subscriptionPaymentMethod,
          },
        });
        console.log('paymentIntent', paymentIntent);
        if (paymentIntent) {
          createUser(
            customerId,
            subscriptionId,
            subscriptionStart,
            subscriptionEnd,
          );
        } else if (error) {
          alert(`Payment Error`);
          setSaving(false);
        }
      }
    } catch (e) {
      alert(`Payment Error 2 ` + e?.message);
      setSaving(false);
    }
  };

Backend Code:

router.post("/makePayment", async (req, res) => {
  try {
    const { email, name, paymentMethod } = req.body;
    if (email === "" || name === "" || !email || !name || !paymentMethod)
      throw "";

    // making new customer
    const customer = await addNewCustomer(email, name, paymentMethod);

    // making new subscription
    const subscription = await addSubscription(customer?.id);

    // getting stripe secret
    const { error, clientSecret } = await getStripeSecret(
      subscription?.latest_invoice?.payment_intent?.payment_method,
      customer?.id
    );

    // saving data in session
    req.session.customerId = customer?.id;
    req.session.subscriptionId = subscription?.id;

    res
      .send({
        clientSecret,
        error,
        customerId: customer?.id,
        subscriptionId: subscription?.id,
        subscriptionPaymentMethod:
          subscription?.latest_invoice?.payment_intent?.payment_method,
        subscriptionStart: new Date(subscription.current_period_start * 1000),
        subscriptionEnd: new Date(subscription.current_period_end * 1000),
      })
      .status(!error ? 200 : 500);
  } catch (e) {
    res.send({ error: "Internal Server Error" }).status(500);
  }
});

Backend Functions:

import Stripe from "stripe";
const { STRIPE_SECRET_KEY, STRIPE_API_VERSION } = process?.env;
const stripe = Stripe(STRIPE_SECRET_KEY, { apiVersion: STRIPE_API_VERSION });

export const getStripeSecret = async (paymentMethod, customerId) => {
  try {
    const paymentIntent = await stripe.paymentIntents.create({
      amount: 28000, //lowest denomination of particular currency
      currency: "aud",
      payment_method_types: ["card"], //by default
      payment_method: paymentMethod,
      customer: customerId,
    });
    const clientSecret = paymentIntent?.client_secret;

    return { clientSecret };
  } catch (e) {
    return { error: e?.message };
  }
};

export const addNewCustomer = async (email, name, paymentMethod) => {
  try {
    const customer = await stripe.customers.create({
      email,
      description: name,
      payment_method: paymentMethod,
      invoice_settings: {
        default_payment_method: paymentMethod,
      },
    });
    return customer;
  } catch (e) {
    return { error: e?.message };
  }
};

export const addSubscription = async (customerId) => {
  try {
    const subscription = await stripe.subscriptions.create({
      customer: customerId,
      items: [{ price: process.env.STRIPE_PRICE_ID }],
      payment_settings: {
        payment_method_options: {
          card: {
            request_three_d_secure: "automatic",
          },
        },
        payment_method_types: ["card"],
        save_default_payment_method: "on_subscription",
      },
      expand: ["latest_invoice.payment_intent"],
    });
    return subscription;
  } catch (e) {
    return { error: e?.message };
  }
};

I tried following the above-shared code. I would want to have my subscription active, just like without 3D Secure.

Without 3D secure:
无法使用Stripe 3D Secure和React Native创建活动订阅(循环订阅)。

With 3D secure
无法使用Stripe 3D Secure和React Native创建活动订阅(循环订阅)。

答案1

得分: 1

你不应该在getStripeSecret函数中创建和使用新的PaymentIntent。你已经有一个PaymentIntent,就是subscription.latest_invoice.payment_intent。你应该获取并返回该PaymentIntent的client_secret,并在前端使用它。根据你当前的代码,你正在处理两笔独立的支付(因此会出现重复),而你在前端处理的那笔支付与订阅没有任何关联(因此它保持不活跃)。你应该仔细阅读并准确实现以下指南:

https://stripe.com/docs/billing/subscriptions/build-subscriptions?ui=elements

但是作为一个开始,你的代码应该更像这样:

// 创建新的订阅
const subscription = await addSubscription(customer?.id); 
// 获取 stripe secret
const clientSecret = subscription.latest_invoice.payment_intent.client_secret;
...
英文:

You shouldn't be creating and using a new PaymentIntent in getStripeSecret. You already have a PaymentIntent — it's the one available in subscription.latest_invoice.payment_intent. You should be getting and returning the client_secret from that one and using it on the frontend. With your current code you are processing two separate payments(hence the duplicates) and the one you process on the frontend is not connected to the Subscription at all(hence why it remains inactive). You should carefully read and exactly implement the guide at

https://stripe.com/docs/billing/subscriptions/build-subscriptions?ui=elements
But as a start your code should be more like this:

// making new subscription
const subscription = await addSubscription(customer?.id); 
// getting stripe secret
const clientSecret = subscription.latest_invoice.payment_intent.client_secret;
...

huangapple
  • 本文由 发表于 2023年7月27日 16:50:29
  • 转载请务必保留本文链接:https://go.coder-hub.com/76778051.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定