我在Django应用程序中使用Stripe。我有以下测试案例:incorrect_cvc
导致card_error。现在,在更正CVC并使用4242 4242 4242 4242
时,我除外,这是一次成功的充电。但是,我得到的是以下错误消息:
请求req_auTSTGSGoUVNUa:幂等请求的密钥只能是 与最初使用的参数相同。尝试使用 如果要执行其他操作,则除“ k1qjchgqjw”外的其他键 请求。
我不知道我更改了哪个parameters
。但是我认为,在card_error
之后,结帐过程基本上不再起作用的想法不是。有谁知道我“更改”了哪些参数导致出现此错误消息?
def checkout_page(request):
"""
* Check if session and ReservedItem exist.
* Generate order_item dict for every ReservedItem entry, that belongs
to order_reference.
If request.method is 'POST':
* Check if ticket reservation is still valid.
* Create entries in models OrderItem, Order & ReservedItem.
"""
session_order_reference = request.session.get('order_reference')
if request.session.get('order_reference'):
reserved_items = ReservedItem.objects.filter(
order_reference=session_order_reference
)
if not reserved_items:
return redirect('website:index')
else:
return redirect('website:index')
taxes_dict = {}
total_gross = total_tax_amount = 0
order_items_list = []
for item in reserved_items:
event = item.ticket.event
timestamp_of_reservation = item.created
total_gross += item.subtotal
order_item = {
'ticket': item.ticket,
'ticket_name': item.ticket.name,
'quantity': item.quantity,
'subtotal': item.subtotal,
'type': OrderType.ORDER,
}
total_tax_amount += add_tax(
item=item,
taxes_dict=taxes_dict,
order_item=order_item,
)
order_items_list.append(dict(order_item))
total_net = total_gross - total_tax_amount # TODO Marc: Calculate in add_vat func?
if request.method == 'POST':
# TODO Marc: Should live in forms.py or just models?
reservation_expired_redirect = check_if_reservation_expired(
request=request,
timestamp_of_reservation=timestamp_of_reservation,
organizer=event.organizer.slug,
event=event.slug,
)
if reservation_expired_redirect:
return reservation_expired_redirect
# TODO Marc: Should live in forms.py or just models?
ticket_is_on_sale = check_if_ticket_is_on_sale(
order_items_list=order_items_list,
request=request,
organizer=event.organizer.slug,
event=event.slug,
)
if ticket_is_on_sale:
return ticket_is_on_sale
billing = BillingForm(request.POST, prefix='billing')
order = OrderForm(request.POST, prefix='order')
if order.is_valid() and billing.is_valid():
# Charge via Stripe
stripe.api_key = "ABC" # TODO Marc: Change to env
token = request.POST.get('stripeToken')
# https://stripe.com/docs/api#error_handling
paid = False
try:
# Compare with transactions > models copy.py > class ChargeManager(models.Manager):
# Use Stripe's library to make requests...
total_gross_amount_in_smallest_unit = smallest_currency_unit(total_gross, 'eur') #TODO Marc: Replace eur
charge = stripe.Charge.create(
amount=total_gross_amount_in_smallest_unit, # TODO Marc > https://stripe.com/docs/currencies#zero-decimal
application_fee=100, # TODO Marc: Which currency?
currency='eur', # TODO Marc
source=token,
stripe_account="ABC", # TODO Marc: Replace with organizer stripe account
idempotency_key=session_order_reference,
)
new_charge_obj = Charge.objects.create(
amount=charge.amount,
charge_id=charge.id,
livemode=charge.livemode,
paid=charge.paid,
refunded=charge.refunded,
currency=charge.currency,
failure_code=charge.failure_code,
failure_message=charge.failure_message,
fraud_details=charge.fraud_details,
outcome=charge.outcome,
status=charge.status,
application_fee=charge.application_fee,
captured=charge.captured,
created=charge.created,
# TODO Marc: Add refunds:
# amount_refunded=charge.amount_refunded,
# etc.
)
application_fee = stripe.ApplicationFee.retrieve(charge.application_fee)
Fee.objects.create(
fee_id=application_fee.id,
livemode=application_fee.livemode,
currency=application_fee.currency,
amount=application_fee.amount,
charge=new_charge_obj,
# TODO Marc: Add refunds
)
paid = new_charge_obj.paid
except stripe.error.CardError as e:
# Since it's a decline, stripe.error.CardError will be caught
body = e.json_body
err = body.get('error', {})
messages.add_message(
request,
messages.ERROR,
err.get('message')
)
# return redirect(
# 'orders:order-list',
# order_reference=new_order.order_reference,
# access_key=new_order.access_key,
# )
# print("Type is: %s") % err.get('type')
# print("Code is: %s") % err.get('code')
# # param is '' in this case
# print("Param is: %s") % err.get('param')
# print("Message is: %s") % err.get('message')
except stripe.error.RateLimitError as e:
# Too many requests made to the API too quickly
pass
except stripe.error.InvalidRequestError as e:
# Invalid parameters were supplied to Stripe's API
pass
except stripe.error.AuthenticationError as e:
# Authentication with Stripe's API failed
# (maybe you changed API keys recently)
pass
except stripe.error.APIConnectionError as e:
# Network communication with Stripe failed
pass
except stripe.error.StripeError as e:
# Display a very generic error to the user, and maybe send
# yourself an email
pass
except Exception as e:
# Something else happened, completely unrelated to Stripe
pass
if paid:
# Create new attendee
i = 1
attendee_list = []
for item in reserved_items:
for _ in range(item.quantity): # noqa
new_attendee_dict = {
'event': item.ticket.event,
'ticket': item.ticket,
'ticket_name': item.ticket.name,
'ticket_reference': session_order_reference + "-" + str(i),
'ticket_code': get_random_string(length=10),
}
i += 1
attendee_list.append(dict(new_attendee_dict))
# Create new order
new_order_dict = {
'total_gross': total_gross,
'total_tax': total_tax_amount,
'total_net': total_net,
'total_gross_converted': total_gross, # TODO Marc
'event': event,
'order_reference': session_order_reference,
'status': OrderStatus.PENDING,
'access_key': get_random_string(length=10),
}
new_order = order.save(commit=False)
[setattr(new_order, k, v) for k, v in new_order_dict.items()]
new_order.save()
# Create order items
for item in order_items_list:
OrderItem.objects.create(order=new_order, **item)
# Create attendees
for item in attendee_list:
Attendee.objects.create(order=new_order, **item)
# Create billing profile
billing_profile = billing.save(commit=False)
billing_profile.order = new_order
billing_profile.save()
# Delete order_reference session
del request.session['order_reference']
return redirect(
'orders:order-list',
order_reference=new_order.order_reference,
access_key=new_order.access_key,
)
else:
billing = BillingForm(prefix='billing')
order = OrderForm(prefix='order')
context = {
'reserved_items': reserved_items,
'taxes': taxes_dict,
'total_net': total_net,
'total_gross': total_gross,
'currency': event.currency,
'order': order,
'billing': billing,
}
return render(request, 'checkout/checkout.html', context)
答案 0 :(得分:1)
有一个类似的问题,我通过indempotency_key
,并且客户的卡被拒后,客户无法付款,因为发送的数据是唯一的费用,而不是卡上的唯一数据。例如,如果他们的CVC错误,那么将使用完全相同的幂等性密钥来创建后续费用,因为未考虑与实际卡相关的数据。
此问题的解决方法是确保您的钥匙对费用唯一,并且在这种情况下,包括卡令牌在内的卡都可以解决此问题。
其他需要考虑的事情包括部分付款,退款,相同/不同的IP,其他元数据。
答案 1 :(得分:0)
问题不在于您所做的任何更改,而在于您未更改的内容:)
在这一行上,您正在传递idempotency_key
:
charge = stripe.Charge.create(
...
idempotency_key=session_order_reference,
)
如Stripe docs中所述,您可以将幂等密钥与请求一起传递,这样将来您可以使用相同的密钥再次提出相同的请求,并且获得与相同的结果。第一个请求。如果您由于网络问题而未收到第一响应,这很有用。
在这种情况下,您已经更改了CVC,它创建了一个新的token
变量。这意味着您的请求与使用相同幂等键的先前请求不同。这是没有意义的,因为您只能对相同的请求使用相同的幂等性密钥,因此您会从Stripe中得到此错误。
要解决此问题,您应该使用新生成的幂等性密钥重试电荷创建。通常,应在应用程序创建的每个唯一请求上生成密钥。
答案 2 :(得分:0)
条带处理这种情况 当您提交错误的简历时
我使用条纹测试信用卡进行了测试 https://stripe.com/docs/testing#cards 使用失败的验证码而不是使用有效卡。