在 django-oscar 中集成包含重定向的支付方式
Integrating a redirection-included method of payment in django-oscar
我正在使用 django-oscar 框架开发一个购物网站,实际上我正在使用他们的沙盒网站。我想在结帐流程中添加付款,但问题是,我完全糊涂了!
我读过这个link:"Oscar's payment integration docs"
我得到了大局。我也在结帐应用程序中阅读了文件 views.py,但我有几个问题在网上找不到。
我的问题是 methods/classes 我应该覆盖或创建什么来处理以下过程:
用户请求付款后,我应该向银行发送请求并给他们所需的参数(代码中的pay_request_parameters)
然后,他们会发送一个 ID,确认我的访问权限,然后我应该 post 将该 ID 发送到一个地址,并将用户重定向到银行的网页。
用户成功向银行付款后,他们会通过我在第一步提供的回调url post 通知我。
有了这些信息,我应该验证付款是否成功,如果成功,我应该要求银行结算,然后把钱寄给我。
现在我的代码完成了前两个步骤,但我不知道如何处理沙箱中重定向后的过程。这是我的代码:
from oscar.apps.checkout import views
from oscar.apps.payment import models
from oscar.apps.payment.exceptions import *
import requests
import datetime
mellat_services_url = 'https://bpm.shaparak.ir/pgwchannel/services/pgw?wsdl'
start_pay_url = 'https://bpm.shaparak.ir/pgwchannel/startpay.mellat'
terminal_id = 'xxx'
username = 'xxx'
password = 'xxx'
# Subclass the core Oscar view so we can customise
class PaymentDetailsView(views.PaymentDetailsView):
def handle_payment(self, order_number, total, **kwargs):
# Talk to payment gateway. If unsuccessful/error, raise a
# PaymentError exception which we allow to percolate up to be caught
# and handled by the core PaymentDetailsView.
# mellat cycle start
local_date = str(datetime.date.today())[0:4] + str(datetime.date.today())[5:7] + str(datetime.date.today())[8:10]
local_time = str(datetime.datetime.now().time())[0:2] + str(datetime.datetime.now().time())[3:5] + str(datetime.datetime.now().time())[6:8]
# call bpPayRequest and get refId
pay_request_parameters = {'terminalId': terminal_id, 'userName': username,
'userPassword': password, 'orderId': order_number,
'amount': total.incl_tax, 'localDate': local_date,
'localTime': local_time, 'additionalData': ""
'callBackUrl': 'mysite.com/checkout/preview/'}
pay_request_answer = requests.post(mellat_services_url, pay_request_parameters)
if not pay_request_answer.split(",")[0] == 0:
response_code = pay_request_answer.split(",")[0]
if response_code[0] == '1':
raise UnableToTakePayment()
else:
raise PaymentError()
requests.post(start_pay_url, pay_request_answer.split(",")[1])
raise RedirectRequired(start_pay_url)
# post the refId to bank and then redirect customer to the bank
# apparently wait for the bank ( like for 10 mins ) to get the payment status
# if the bank responded with success, the you verify the payment with a post to the bank
# if everything was verified, tell the bank for a settlement
# mellat cycle end
#The rest should be implemented but I dont know where I should put this
#All I know is that it should be done after the verification with the data
#sent from the bank.
reference = gateway.pre_auth(order_number, total.incl_tax, kwargs['bankcard'])
# Payment successful! Record payment source
source_type, __ = models.SourceType.objects.get_or_create(
name="SomeGateway")
source = models.Source(
source_type=source_type,
amount_allocated=total.incl_tax,
reference=reference)
self.add_payment_source(source)
# Record payment event
self.add_payment_event('pre-auth', total.incl_tax)
提前致谢。
我有一个类似的问题,我所做的是将来自银行的 callBackUrl 重定向到一个视图,该视图的实现方式如下:
class CustomCheckoutDone(OrderPlacementMixin, RedirectView):
"""
here we verify payment was done and place the actual order
then redirect to thank you page
"""
permanent = False
def get_redirect_url(self, pk):
basket = Basket.objects.get(pk=self.checkout_session.get_submitted_basket_id())
basket.strategy = CustomStrategy()
order_number = self.checkout_session.get_order_number()
shipping_address = self.get_shipping_address(basket)
shipping_method = self.get_shipping_method(basket, shipping_address)
shipping_charge = shipping_method.calculate(basket)
billing_address = self.get_billing_address(shipping_address)
order_total = self.get_order_totals(basket, shipping_charge=shipping_charge)
order_kwargs = {}
# make sure payment was actually paid
CustomPayment.objects.get(order_number=order_number, payed_sum=str(float(order_total.incl_tax)))
user = self.request.user
if not user.is_authenticated():
order_kwargs['guest_email'] = self.checkout_session.get_guest_email()
self.handle_order_placement(
order_number, user, basket, shipping_address, shipping_method,
shipping_charge, billing_address, order_total, **order_kwargs
)
return '/checkout/thank-you/'
好吧,我遇到了同样的问题,解决方案如下:
当我们重定向到支付网关时,我们应该有回调 URL 用于批准、拒绝和取消。
首先,我们应该将这些添加到 settings.py
SESSION_COOKIE_SAMESITE = None
SESSION_COOKIE_DOMAIN = 'localhost' #change it to your domain in prod env.
如文档所述,付款完成后我们需要做一些额外的事情:
https://django-oscar.readthedocs.io/en/2.0.4/howto/how_to_integrate_payment.html
您在上面所写的一切都是正确的。
之后设置回调URL:
path('gateway/', PaymentReturnURL.as_view(), name="gateway")
from django.shortcuts import render
from oscar.apps.checkout.mixins import OrderPlacementMixin
from django.views.generic.base import TemplateView
from django.views.decorators.csrf import csrf_exempt
from django.utils.decorators import method_decorator
from django.shortcuts import redirect
from django.urls.base import reverse_lazy
from oscar.core.loading import get_model
from datetime import datetime
from oscar.apps.payment import models
from xml.etree import ElementTree
Basket = get_model('basket', 'Basket')
@method_decorator(csrf_exempt, name='dispatch')
class PaymentReturnURL(OrderPlacementMixin,TemplateView):
success_url = reverse_lazy('checkout:thank-you')
basket_url = reverse_lazy('basket:summary')
def get_success_url(self):
return self.success_url
def get_order_details(self):
basket = Basket.objects.get(pk=self.checkout_session.get_submitted_basket_id())
basket.strategy = self.request.strategy
order_number = self.checkout_session.get_order_number()
shipping_address = self.get_shipping_address(basket)
shipping_method = self.get_shipping_method(basket, shipping_address)
shipping_charge = shipping_method.calculate(basket)
billing_address = self.get_billing_address(shipping_address)
order_total = self.get_order_totals(basket, shipping_charge=shipping_charge)
order_kwargs = {}
return {
"basket": basket,
"order_number": order_number,
"shipping_address": shipping_address,
"shipping_method": shipping_method,
"shipping_charge": shipping_charge,
"billing_address": billing_address,
"order_total": order_total,
"order_kwargs": order_kwargs,
}
def get(self, request, *args, **kwargs):
return redirect(self.get_success_url())
def post(self, request, *args, **kwargs):
#for my case it was XML I needed to parse, after proceding from bank, banks posts the data to your callback url
context = {}
data = self.request.POST.get("xmlmsg")
xml_response = ElementTree.fromstring(data)
for i in xml_response.iter("*"):
context[i.tag] = i.text
status = context.get("OrderStatus")
if status == "APPROVED":
# Payment successful! Record payment source
user = self.request.user
print("the user",user)
order_details = self.get_order_details()
source_type, __ = models.SourceType.objects.get_or_create(name="Name of Payment")
source = models.Source(
source_type=source_type,
amount_allocated=context.get("PurchaseAmountScr"),
reference=context.get("OrderID"))
self.add_payment_source(source)
# Record payment event
self.add_payment_event('pre-auth', float(context.get("PurchaseAmountScr")))
return self.handle_order_placement(
order_details['order_number'], user, order_details['basket'], order_details['shipping_address'],
order_details['shipping_method'], order_details['shipping_charge'],
order_details['billing_address'], order_details['order_total'], **order_details['order_kwargs']
)
#for cancel situation
elif status == "CANCELED":
self.restore_frozen_basket()
return redirect("basket:summary")
#for decline stiuation
elif status == "DECLINED":
self.restore_frozen_basket()
return redirect("basket:summary")
def restore_frozen_basket(self):
"""
Restores a frozen basket as the sole OPEN basket. Note that this also
merges in any new products that have been added to a basket that has
been created while payment.
"""
try:
fzn_basket = self.get_submitted_basket()
except Basket.DoesNotExist:
# Strange place. The previous basket stored in the session does
# not exist.
pass
else:
fzn_basket.thaw()
if self.request.basket.id != fzn_basket.id:
fzn_basket.merge(self.request.basket)
# Use same strategy as current request basket
fzn_basket.strategy = self.request.basket.strategy
self.request.basket = fzn_basket
在您 post 之后,您将被重定向到感谢页面。
另一种方法:使用支付服务的网络挂钩(IPN,随便什么)来验证(并在 oscar 系统中添加)实际支付,然后 return URL 完成下单处理,然后重定向到感谢页面。
因此,即使用户没有返回 return URL(发生在我们关闭“您将在一秒钟内被重定向到商家”页面!),订单将被正确放置,因为我们也可以在网络挂钩中检查订单状态,并在需要时先完成订单放置。反过来也是可能的(webhook 首先,returnURL 不需要再做任何事情了)。
我有意没有添加太多实际代码,因为其他人已经提供了这些代码。我仍在研究如何使用尽可能多的 DRY 来实现所有这些。
urls.py
path('payment-capture/', PaymentCaptureView.as_view(), name="payment-capture")
path('payment-return/', PaymentReturnView.as_view(), name="payment-return")
views.py
class PaymentCaptureView(OrderPlacementMixin, RedirectView):
"""
check for successfull payment,
finish order placement, if needed
register payment
notify payment service with 'ok' ;-)=
"""
permanent = False
def post(self, pk):
# check if we actually really received the payment, abort otherwise
# check if the order is already placed (via return url?)
# if not, place the order
# and finally add a the payment in oscar
return 'ok'
class PaymentReturnView(OrderPlacementMixin, RedirectView):
"""
finish order placement, redirect to thank you
"""
permanent = False
def get_redirect_url(self, pk):
# check if the order is already placed
# if not, place the order
return reverse('checkout:thank-you')
我正在使用 django-oscar 框架开发一个购物网站,实际上我正在使用他们的沙盒网站。我想在结帐流程中添加付款,但问题是,我完全糊涂了!
我读过这个link:"Oscar's payment integration docs"
我得到了大局。我也在结帐应用程序中阅读了文件 views.py,但我有几个问题在网上找不到。
我的问题是 methods/classes 我应该覆盖或创建什么来处理以下过程:
用户请求付款后,我应该向银行发送请求并给他们所需的参数(代码中的pay_request_parameters)
然后,他们会发送一个 ID,确认我的访问权限,然后我应该 post 将该 ID 发送到一个地址,并将用户重定向到银行的网页。
用户成功向银行付款后,他们会通过我在第一步提供的回调url post 通知我。
有了这些信息,我应该验证付款是否成功,如果成功,我应该要求银行结算,然后把钱寄给我。
现在我的代码完成了前两个步骤,但我不知道如何处理沙箱中重定向后的过程。这是我的代码:
from oscar.apps.checkout import views
from oscar.apps.payment import models
from oscar.apps.payment.exceptions import *
import requests
import datetime
mellat_services_url = 'https://bpm.shaparak.ir/pgwchannel/services/pgw?wsdl'
start_pay_url = 'https://bpm.shaparak.ir/pgwchannel/startpay.mellat'
terminal_id = 'xxx'
username = 'xxx'
password = 'xxx'
# Subclass the core Oscar view so we can customise
class PaymentDetailsView(views.PaymentDetailsView):
def handle_payment(self, order_number, total, **kwargs):
# Talk to payment gateway. If unsuccessful/error, raise a
# PaymentError exception which we allow to percolate up to be caught
# and handled by the core PaymentDetailsView.
# mellat cycle start
local_date = str(datetime.date.today())[0:4] + str(datetime.date.today())[5:7] + str(datetime.date.today())[8:10]
local_time = str(datetime.datetime.now().time())[0:2] + str(datetime.datetime.now().time())[3:5] + str(datetime.datetime.now().time())[6:8]
# call bpPayRequest and get refId
pay_request_parameters = {'terminalId': terminal_id, 'userName': username,
'userPassword': password, 'orderId': order_number,
'amount': total.incl_tax, 'localDate': local_date,
'localTime': local_time, 'additionalData': ""
'callBackUrl': 'mysite.com/checkout/preview/'}
pay_request_answer = requests.post(mellat_services_url, pay_request_parameters)
if not pay_request_answer.split(",")[0] == 0:
response_code = pay_request_answer.split(",")[0]
if response_code[0] == '1':
raise UnableToTakePayment()
else:
raise PaymentError()
requests.post(start_pay_url, pay_request_answer.split(",")[1])
raise RedirectRequired(start_pay_url)
# post the refId to bank and then redirect customer to the bank
# apparently wait for the bank ( like for 10 mins ) to get the payment status
# if the bank responded with success, the you verify the payment with a post to the bank
# if everything was verified, tell the bank for a settlement
# mellat cycle end
#The rest should be implemented but I dont know where I should put this
#All I know is that it should be done after the verification with the data
#sent from the bank.
reference = gateway.pre_auth(order_number, total.incl_tax, kwargs['bankcard'])
# Payment successful! Record payment source
source_type, __ = models.SourceType.objects.get_or_create(
name="SomeGateway")
source = models.Source(
source_type=source_type,
amount_allocated=total.incl_tax,
reference=reference)
self.add_payment_source(source)
# Record payment event
self.add_payment_event('pre-auth', total.incl_tax)
提前致谢。
我有一个类似的问题,我所做的是将来自银行的 callBackUrl 重定向到一个视图,该视图的实现方式如下:
class CustomCheckoutDone(OrderPlacementMixin, RedirectView):
"""
here we verify payment was done and place the actual order
then redirect to thank you page
"""
permanent = False
def get_redirect_url(self, pk):
basket = Basket.objects.get(pk=self.checkout_session.get_submitted_basket_id())
basket.strategy = CustomStrategy()
order_number = self.checkout_session.get_order_number()
shipping_address = self.get_shipping_address(basket)
shipping_method = self.get_shipping_method(basket, shipping_address)
shipping_charge = shipping_method.calculate(basket)
billing_address = self.get_billing_address(shipping_address)
order_total = self.get_order_totals(basket, shipping_charge=shipping_charge)
order_kwargs = {}
# make sure payment was actually paid
CustomPayment.objects.get(order_number=order_number, payed_sum=str(float(order_total.incl_tax)))
user = self.request.user
if not user.is_authenticated():
order_kwargs['guest_email'] = self.checkout_session.get_guest_email()
self.handle_order_placement(
order_number, user, basket, shipping_address, shipping_method,
shipping_charge, billing_address, order_total, **order_kwargs
)
return '/checkout/thank-you/'
好吧,我遇到了同样的问题,解决方案如下:
当我们重定向到支付网关时,我们应该有回调 URL 用于批准、拒绝和取消。
首先,我们应该将这些添加到 settings.py
SESSION_COOKIE_SAMESITE = None
SESSION_COOKIE_DOMAIN = 'localhost' #change it to your domain in prod env.
如文档所述,付款完成后我们需要做一些额外的事情:
https://django-oscar.readthedocs.io/en/2.0.4/howto/how_to_integrate_payment.html
您在上面所写的一切都是正确的。
之后设置回调URL:
path('gateway/', PaymentReturnURL.as_view(), name="gateway")
from django.shortcuts import render
from oscar.apps.checkout.mixins import OrderPlacementMixin
from django.views.generic.base import TemplateView
from django.views.decorators.csrf import csrf_exempt
from django.utils.decorators import method_decorator
from django.shortcuts import redirect
from django.urls.base import reverse_lazy
from oscar.core.loading import get_model
from datetime import datetime
from oscar.apps.payment import models
from xml.etree import ElementTree
Basket = get_model('basket', 'Basket')
@method_decorator(csrf_exempt, name='dispatch')
class PaymentReturnURL(OrderPlacementMixin,TemplateView):
success_url = reverse_lazy('checkout:thank-you')
basket_url = reverse_lazy('basket:summary')
def get_success_url(self):
return self.success_url
def get_order_details(self):
basket = Basket.objects.get(pk=self.checkout_session.get_submitted_basket_id())
basket.strategy = self.request.strategy
order_number = self.checkout_session.get_order_number()
shipping_address = self.get_shipping_address(basket)
shipping_method = self.get_shipping_method(basket, shipping_address)
shipping_charge = shipping_method.calculate(basket)
billing_address = self.get_billing_address(shipping_address)
order_total = self.get_order_totals(basket, shipping_charge=shipping_charge)
order_kwargs = {}
return {
"basket": basket,
"order_number": order_number,
"shipping_address": shipping_address,
"shipping_method": shipping_method,
"shipping_charge": shipping_charge,
"billing_address": billing_address,
"order_total": order_total,
"order_kwargs": order_kwargs,
}
def get(self, request, *args, **kwargs):
return redirect(self.get_success_url())
def post(self, request, *args, **kwargs):
#for my case it was XML I needed to parse, after proceding from bank, banks posts the data to your callback url
context = {}
data = self.request.POST.get("xmlmsg")
xml_response = ElementTree.fromstring(data)
for i in xml_response.iter("*"):
context[i.tag] = i.text
status = context.get("OrderStatus")
if status == "APPROVED":
# Payment successful! Record payment source
user = self.request.user
print("the user",user)
order_details = self.get_order_details()
source_type, __ = models.SourceType.objects.get_or_create(name="Name of Payment")
source = models.Source(
source_type=source_type,
amount_allocated=context.get("PurchaseAmountScr"),
reference=context.get("OrderID"))
self.add_payment_source(source)
# Record payment event
self.add_payment_event('pre-auth', float(context.get("PurchaseAmountScr")))
return self.handle_order_placement(
order_details['order_number'], user, order_details['basket'], order_details['shipping_address'],
order_details['shipping_method'], order_details['shipping_charge'],
order_details['billing_address'], order_details['order_total'], **order_details['order_kwargs']
)
#for cancel situation
elif status == "CANCELED":
self.restore_frozen_basket()
return redirect("basket:summary")
#for decline stiuation
elif status == "DECLINED":
self.restore_frozen_basket()
return redirect("basket:summary")
def restore_frozen_basket(self):
"""
Restores a frozen basket as the sole OPEN basket. Note that this also
merges in any new products that have been added to a basket that has
been created while payment.
"""
try:
fzn_basket = self.get_submitted_basket()
except Basket.DoesNotExist:
# Strange place. The previous basket stored in the session does
# not exist.
pass
else:
fzn_basket.thaw()
if self.request.basket.id != fzn_basket.id:
fzn_basket.merge(self.request.basket)
# Use same strategy as current request basket
fzn_basket.strategy = self.request.basket.strategy
self.request.basket = fzn_basket
在您 post 之后,您将被重定向到感谢页面。
另一种方法:使用支付服务的网络挂钩(IPN,随便什么)来验证(并在 oscar 系统中添加)实际支付,然后 return URL 完成下单处理,然后重定向到感谢页面。
因此,即使用户没有返回 return URL(发生在我们关闭“您将在一秒钟内被重定向到商家”页面!),订单将被正确放置,因为我们也可以在网络挂钩中检查订单状态,并在需要时先完成订单放置。反过来也是可能的(webhook 首先,returnURL 不需要再做任何事情了)。
我有意没有添加太多实际代码,因为其他人已经提供了这些代码。我仍在研究如何使用尽可能多的 DRY 来实现所有这些。
urls.py
path('payment-capture/', PaymentCaptureView.as_view(), name="payment-capture")
path('payment-return/', PaymentReturnView.as_view(), name="payment-return")
views.py
class PaymentCaptureView(OrderPlacementMixin, RedirectView):
"""
check for successfull payment,
finish order placement, if needed
register payment
notify payment service with 'ok' ;-)=
"""
permanent = False
def post(self, pk):
# check if we actually really received the payment, abort otherwise
# check if the order is already placed (via return url?)
# if not, place the order
# and finally add a the payment in oscar
return 'ok'
class PaymentReturnView(OrderPlacementMixin, RedirectView):
"""
finish order placement, redirect to thank you
"""
permanent = False
def get_redirect_url(self, pk):
# check if the order is already placed
# if not, place the order
return reverse('checkout:thank-you')