2

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

根据 Stripe 的文档:

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

当我查看 call 的响应时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、附加PaymentMethodCustomer和创建 的代码Subscription。如果我的代码中有任何错误,请告诉我。

设置意图:

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);
}
4

1 回答 1

7

我联系了 Stripe 支持。他们告诉我,我使用的测试卡的目的是要求对每笔交易进行 3DS 身份验证。这意味着即使我在验证后保存了卡,除非经过验证,否则每次使用都会拒绝该卡。所以我的解决方案是使用不同的测试卡,一张只需要验证一次,之后每次使用都会获得批准。

此外,根据我在此处阅读的有关 SCA 的一些文档,SCA 所需卡上的经常性交易是免税的,并且只需要一次身份验证。因此,建议使用只需要验证一次的测试卡,因为它的行为更接近于 SCA 开始实施时的预期。

于 2019-08-09T22:28:55.243 回答