分条订阅-向3D安全卡号收费,返回subscription_payment_intent_requires_action

时间:2019-08-01 23:22:33

标签: javascript java stripe-payments 3d-secure

我们正在将3D安全结帐与我们当前的订阅计费解决方案集成在一起。我们提供7个月免费试用的月租计划。对于集成,我们使用SetupIntent确定客户的卡信息是否需要3D安全授权。使用需要3D安全的Stripe测试卡,我们在结帐页面的javascript中调用handleCardSetup()函数。然后,在成功授权之后,将客户的付款方式附加到客户。然后继续结帐,我们创建订阅。我们在订阅上展开latest_invoice.payment_intent

根据Stripe的文档:

“ SetupIntents是自动为不需要初始付款的订阅创建的。如果需要身份验证和授权,则也会执行它们。如果成功或不需要,则不需要任何操作,并且subscription.pending_setup_intent字段为空。”

当我查看调用Subscription.create(params)的响应时,发现pending_setup_intent字段等于null。但是,当在Stripe的仪表板上查看预订时,我看到尝试对卡充值的尝试返回了402错误,并显示以下响应:

{
  "error": {
    "code": "subscription_payment_intent_requires_action",
    "message": "Payment for this subscription requires additional user action before it can be completed successfully. Please refer to the use of the `enable_incomplete_payments` parameter here: https://stripe.com/docs/billing/lifecycle#incomplete-opt-in",
    "type": "card_error"
  }
}

有什么作用?我在某个地方错过了一步吗?当前,我们正在使用Stripe API的最新版本:2019-05-16。我已经附加了用于创建SetupIntent,将PaymentMethod附加到Customer以及创建Subscription的代码。如果我的代码中有任何错误,请告诉我。

SetupIntent:

public static String createSetupIntent() {
    try {
        Map<String, Object> setupIntentParams = new HashMap<>();
        ArrayList<String> paymentMethodTypes = new ArrayList<>();
        paymentMethodTypes.add("card");
        setupIntentParams.put("payment_method_types", paymentMethodTypes);
        SetupIntent setupIntent = SetupIntent.create(setupIntentParams);
        return setupIntent.getClientSecret();
    } catch (AuthenticationException | InvalidRequestException | ApiConnectionException | ApiException ex) {
        throw new Error("Unable to create SetupIntent", ex);
    } catch (StripeException ex) {
        throw new Error("Unable to create SetupIntent", ex);
    }
}

Javascript:

var customerInfo = {
  payment_method_data: {
    billing_details: {
      name: document.getElementById('cardholder_name').value,
      address: {
        postal_code: document.getElementById('cardholder_zip').value
      }
    }
  }
};

stripe.handleCardSetup(clientSecret, card, customerInfo).then(function (result) {
    if (result.error) {
        setStripeError(result.error);
        hideLoading('hosted_content');
    } else {
        var paymentMethodId = result.setupIntent.payment_method;

        $.post({
            url: backendURL + 'attachpaymentmethod',
            data: {payment_id: paymentMethodId},
            success: function (response) {
                document.getElementById('payment-form').submit(); //sends a post to endpoint that handles creating subscription
            },
            error: function (response) {
                hideLoading('hosted_content');
                setStripeError(response.responseJSON.error);
            }
        });

    }
});

付款方式:

public static void updatePaymentMethod(User user, String paymentMethodId) throws CardException {
    Customer customer = getCustomer(user); //Retrieves customer if user has stripeId, otherwise create a new customer
    try {
        PaymentMethod paymentMethod = PaymentMethod.retrieve(paymentMethodId);
        Map<String, Object> params = new HashMap<String, Object>();
        params.put("customer", customer.getId());
        paymentMethod.attach(params);
    } catch (AuthenticationException | InvalidRequestException | ApiConnectionException | ApiException ex) {
        throw new Error("Unable to update Stripe payment method", ex);
    } catch (StripeException ex) {
        throw new Error("Unable to update Stripe payment method", ex);
    }
}

订阅:

Map<String, Object> planItem = new HashMap<>();
planItem.put("plan", planId);
Map<String, Object> items = new HashMap<>();
items.put("0", planItem);

List<String> expandList = new LinkedList<String>();
expandList.add("latest_invoice.payment_intent");

Map<String, Object> subscriptionOptions = new HashMap<>();
subscriptionOptions.put("customer", user.getStripeId());
subscriptionOptions.put("trial_period_days", trialDays);
subscriptionOptions.put("items", items);
subscriptionOptions.put("expand", expandList);
try {
    Subscription subscription = Subscription.create(subscriptionOptions);
    System.out.println(subscription); //pending_setup_intent is equal to null
} catch (AuthenticationException | InvalidRequestException | ApiConnectionException | ApiException ex) {
    throw new Error("Unable to create Stripe subscription", ex);
} catch (StripeException ex) {
    throw new Error("Unable to create Stripe subscription", ex);
}

1 个答案:

答案 0 :(得分:1)

我与Stripe支持取得联系。他们告诉我,我使用的测试卡的目的是要求每笔交易都具有3DS身份验证。这意味着即使我在对卡进行身份验证之后也保存了它,除非经过身份验证,否则每次使用该卡都会被拒绝。因此,我的解决方案是使用另一种测试卡,该测试卡只需要进行一次身份验证,然后将在每次使用后得到批准。

此外,根据我读过here的有关SCA的一些文档,在SCA所需卡上的重复交易是免税的,只需要进行一次身份验证。因此,建议使用只需要认证一次的测试卡,因为它的行为更接近于开始执行SCA时的行为。