【问题标题】:Stripe subscription creates duplicate payment on Django/PythonStripe 订阅在 Django/Python 上创建重复付款
【发布时间】:2020-02-17 13:59:30
【问题描述】:

我使用 Django/Stripe SDK 创建了条带订阅,并且由于该软件位于欧洲,因此它使用了新的 SCA(强客户身份验证)。因此,首先我将来源附加给客户,然后我尝试订阅他们,它有效,但对于某些客户,我在 Stripe 控制面板上看到重复付款。

我已联系 Stripe,他们的客户支持推荐了以下内容,但我无法弄清楚:

我明白您所说的客户被收取的费用超过了您的意思
一次。问题归结为 API 调用请求将它们添加到 订阅从您的服务器不止一次发送到 Stripe。

要最终解决这个问题,您需要确保您的系统正在发送 一次请求。我建议检查您的服务器以查看 请求来自。一旦完成,这可能是一个简单的解决方案 确定。


@login_required
def PaymentView(request):
    profile = Profile.objects.filter(user=request.user).first()
    try:
        address = profile.address.get(address_type="home")
    except:
        address = None
    user_membership = get_user_membership(request)
    try:
        selected_membership = get_selected_membership(request)
    except:
        return redirect(reverse("membership:select"))
    publishKey = settings.STRIPE_PUBLISHABLE_KEY
    if request.method == "POST":
        # try:
        source = request.POST.get('stripeSource', "")
        amend = request.POST.get('amend', '')

        '''
        First we need to add the source for the customer
        '''
        if amend == "true":
            customer = stripe.Customer.modify(
                user_membership.stripe_customer_id,
                source=source,
            )
            customer.save()
        else:
            customer = stripe.Customer.retrieve(
                user_membership.stripe_customer_id)

            try:
                customer.source = source  # 4242424242424242
                customer.save()
            except:
                pass

        '''
        Now we can create the subscription using only the customer as we don't need to pass their
        credit card source anymore
        '''

        stripe_subscription = stripe.Subscription.create(
            customer=user_membership.stripe_customer_id,
            items=[
                {"plan": selected_membership.stripe_plan_id},
            ],
            # billing="charge_automatically", #billing is depricated
            collection_method="charge_automatically",
            expand=['latest_invoice.payment_intent'],
            # idempotency_key='FXZMav7BbtEui1Z3',
        )
        # subscription = djstripe.models.Subscription.sync_from_stripe_data(
        #     stripe_subscription
        # )

        if stripe_subscription.status == "active":
            return redirect(reverse('membership:update-transactions',
                                    kwargs={
                                        'subscription_id': stripe_subscription.id
                                    }))
        elif stripe_subscription.status == "incomplete":
            payment_intent = stripe_subscription.latest_invoice.payment_intent
            if payment_intent.status == "requires_action":
                messages.info(
                    request, "Your bank requires extra authentication")
                context = {
                    "client_secret": payment_intent.client_secret,
                    "STRIPE_PUBLIC_KEY": settings.STRIPE_PUBLISHABLE_KEY,
                    "subscription_id": stripe_subscription.id
                }
                return render(request, "membership/3d-secure-checkout.html", context)
            elif payment_intent.status == "requires_payment_method":
                messages.warning(
                    request, "Your card has been failed or declined, Please try again")
                context = {
                    'publishKey': publishKey,
                    'selected_membership': selected_membership,
                    'client_secret': client_secret,
                    'address': address,
                    'profile': profile,
                    'amend': "true"
                }
                return render(request, "membership/membership_payment.html", context)
            else:
                messages.info(
                    request, "Something went wrong. Please report to the website admin.")

    context = {
        'publishKey': publishKey,
        'selected_membership': selected_membership,
        # 'client_secret': client_secret,
        'address': address,
        'profile': profile,
        'amend': "false"
    }

    return render(request, "membership/membership_payment.html", context)


@login_required
def updateTransactionRecords(request, subscription_id):
    user_membership = get_user_membership(request)
    selected_membership = get_selected_membership(request)
    user_membership.membership = selected_membership
    user_membership.save()
    sub, created = Subscription.objects.get_or_create(
        user_membership=user_membership)
    sub.stripe_subscription_id = subscription_id
    sub.active = True
    email_content, e = EmailTemplate.objects.get_or_create(
        email_tag='paid_subscription_success')
    recepient = request.user.email
    sender = settings.EMAIL_HOST_USER
    send_mail(email_content.email_subject, email_content.email_body,
              sender, [recepient])
    sub.save()

    try:
        del request.session['selected_membership_type']
    except:
        pass

    messages.info(request, 'Successfully created {} membership'.format(
        selected_membership))
    return redirect(reverse('membership:select'))


@login_required
def successful_membership(request):
    selected_membership = request.session.get('selected_membership', "Free")

    return render(request, 'membership/purchase_membership_success.html', context={"membership": selected_membership})


这是我的js的一部分:

<script src="https://js.stripe.com/v3/"></script>
  <!-- script for the stripe form -->
  <script type="text/javascript">
  // Create a Stripe client.
    var stripe = Stripe("{{ publishKey }}");

    // Create an instance of Elements.
    var elements = stripe.elements();
    var cardButton = document.getElementById("card-button");
    var cardholderName = document.getElementById("cardholder-name");
    var cardElement = elements.create("card");
    cardElement.mount("#card-element");
    var line1 = document.getElementById("line1");
    var line2 = document.getElementById("line2");
    var city = document.getElementById("city");
    var country = document.getElementById("country");
    var postalCode = document.getElementById("postal_code");
    var email = document.getElementById("email");
    var ownerInfo = {
      owner: {
        name: cardholderName.value,
        address: {
          line1: line1.value,
          line2: line2.value,
          city: city.value,
          postal_code: postalCode.value,
          country: country.value
        },
        email: email.value
      }
    };
    // Add an instance of the card Element into the `card-element` <div>.
    // Handle real-time validation errors from the card Element.
    cardElement.addEventListener("change", function (event) {
      var displayError = document.getElementById("card-errors");
      if (event.error) {
        displayError.textContent = event.error.message;
      } else {
        displayError.textContent = "";
      }
    });
    // Handle form submission.
    var form = document.getElementById("payment-form");
    form.addEventListener("submit", function (event) {
      event.preventDefault();
      stripe.createSource(cardElement, ownerInfo).then(function (result) {
        if (result.error) {
          // Inform the user if there was an error.
          var errorElement = document.getElementById("card-errors");
          errorElement.textContent = result.error.message;
        } else {
          // Send the token to your server.
          setTimeout(stripeSourceHandler(result.source), 3000);
          //stripeSourceHandler(result.source);
        }
      });
    });
    // Submit the form with the source ID.
    function stripeSourceHandler(source) {
      var form = document.getElementById("payment-form");
      var hiddenInput = document.createElement("input");
      hiddenInput.setAttribute("type", "hidden");
      hiddenInput.setAttribute("name", "stripeSource");
      hiddenInput.setAttribute("value", source.id);
      form.appendChild(hiddenInput);
      // Insert the source ID into the form so it gets submitted to the server
      // Submit the form
      form.submit();
    }
  </script>

【问题讨论】:

    标签: python django stripe-payments


    【解决方案1】:

    我已通过在订阅用户之前检查用户当前是否有任何活动订阅来解决此问题,我不确定首先为什么会发生这种情况,但我认为这是由于互联网速度或移动设备上的断开连接,所以 javascript 正在发送请求,但在它重定向用户之前,它会失去连接。

    这是我使用 stripe SDK 修复它的方法:

    customer = stripe.Customer.retrieve(user_membership.stripe_customer_id)
            if customer.subscriptions.total_count > 0:
                for i in customer.subscriptions.data:
                    if i.plan['id'] == selected_membership.stripe_plan_id and i.plan['active'] == True:
                        messages.info(
                            request, "Your have already subscribed to this plan")
                        return redirect(reverse('membership:update-transactions',
                                                kwargs={
                                                    'subscription_id': i.id
                                                }))
                    else:
                        pass  # Maybe we can check if users have a subscription that needs renewing
            else:
    

    如果有人有更简洁的代码,请与我分享。 ?

    【讨论】:

      猜你喜欢
      • 2022-01-03
      • 2016-04-26
      • 2020-01-14
      • 1970-01-01
      • 2015-01-12
      • 2015-01-10
      • 1970-01-01
      • 2017-06-19
      • 2020-09-18
      相关资源
      最近更新 更多