条带订阅在Django/Python上创建重复付款

条带订阅在Django/Python上创建重复付款,python,django,stripe-payments,stripe-subscriptions,Python,Django,Stripe Payments,Stripe Subscriptions,我已经使用Django/stripe SDK创建了一个stripe订阅,因为该软件在欧洲,所以它使用了新的SCA(强客户身份验证)。因此,首先我将源代码附加到客户,然后我尝试订阅它们,这是可行的,但对于某些客户,我在条带控制面板上看到重复付款 我已经联系了Stripe,他们的客户支持建议了以下内容,但我无法理解: 我明白你的意思,当你说客户被收取的费用超过 一旦问题归结为API调用请求将它们添加到 订阅正在从服务器多次发送到条带 要解决这个问题,您需要确保您的系统正在发送 请求只有一次。我建议您

我已经使用Django/stripe SDK创建了一个stripe订阅,因为该软件在欧洲,所以它使用了新的SCA(强客户身份验证)。因此,首先我将源代码附加到客户,然后我尝试订阅它们,这是可行的,但对于某些客户,我在条带控制面板上看到重复付款

我已经联系了Stripe,他们的客户支持建议了以下内容,但我无法理解:

我明白你的意思,当你说客户被收取的费用超过
一旦问题归结为API调用请求将它们添加到 订阅正在从服务器多次发送到条带

要解决这个问题,您需要确保您的系统正在发送 请求只有一次。我建议您检查您的服务器,看看 请求来自于。这可能是一个简单的解决办法,一旦这是一个 下定决心

以下是我的js的一部分:


//创建条带客户端。
var stripe=stripe(“{publishKey}}”);
//创建元素的实例。
var elements=stripe.elements();
var cardButton=document.getElementById(“卡片按钮”);
var CANDERNAME=document.getElementById(“持卡人名称”);
var cardElement=elements.create(“卡片”);
卡元件。安装(“卡元件”);
var line1=document.getElementById(“line1”);
var line2=document.getElementById(“line2”);
var city=document.getElementById(“城市”);
var country=document.getElementById(“国家”);
var postalCode=document.getElementById(“邮政编码”);
var email=document.getElementById(“电子邮件”);
var ownerInfo={
所有者:{
名称:CANDERNAME.value,
地址:{
line1:line1.value,
line2:line2.value,
城市:城市。价值,
邮政编码:postalCode.value,
国家:国家。价值
},
电子邮件:email.value
}
};
//将card元素的实例添加到“card元素”中。
//处理来自卡元素的实时验证错误。
cardElement.addEventListener(“更改”,函数(事件){
var displayError=document.getElementById(“卡错误”);
if(event.error){
displayError.textContent=event.error.message;
}否则{
displayError.textContent=“”;
}
});
//处理表格提交。
var form=document.getElementById(“付款单”);
表单。addEventListener(“提交”,函数(事件){
event.preventDefault();
stripe.createSource(cardElement,ownerInfo).then(函数(结果){
if(result.error){
//如果出现错误,请通知用户。
var errorElement=document.getElementById(“卡错误”);
errorElement.textContent=result.error.message;
}否则{
//将令牌发送到服务器。
setTimeout(stripeSourceHandler(result.source),3000);
//stripeSourceHandler(result.source);
}
});
});
//提交带有源ID的表单。
函数条带SourceHandler(源){
var form=document.getElementById(“付款单”);
var hiddenInput=document.createElement(“输入”);
setAttribute(“类型”、“隐藏”);
setAttribute(“名称”、“stripeSource”);
hiddenInput.setAttribute(“值”,source.id);
表格.appendChild(hiddenInput);
//将源ID插入表单,以便将其提交到服务器
//提交表格
表单提交();
}

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

下面是我如何使用stripe SDK修复它的:

customer=stripe.customer.retrieve(用户\u成员资格.stripe\u客户\u id)
如果customer.subscriptions.total_count>0:
对于customer.subscriptions.data中的i:
如果i.plan['id']==所选的\u membership.stripe\u plan\u id和i.plan['active']==真:
messages.info(
请求,“您已签署本计划”)
返回重定向(反向('成员资格:更新事务',
夸尔斯={
“订阅id”:i.id
}))
其他:
通过#也许我们可以检查用户是否有需要续订的订阅
其他:
如果有人有更干净的代码,请与我分享。

@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})