(from:http://www.cnblogs.com/derek1184405959/p/8859309.html)

十二、支付宝沙箱环境配置

12.1.创建应用

进入蚂蚁金服开放平台(https://open.alipay.com/platform/home.htm),登录后进入管理中心-->>应用列表

 创建应用

创建应用后会有一个appid。还需要提交信息进行审核。微信支付和支付宝支付都是要求企业认证才可以完成的,个人开发不可以,所以我们需要用

沙箱环境,它可以让我们不具备这些应用或者说应用审核还没通过的时候先开发调试

12.2.沙箱环境

沙箱应用地址:https://openhome.alipay.com/platform/appDaily.htm?tab=info

(1)公钥和私钥的生成方法

地址:https://docs.open.alipay.com/291/105971/

选winwods,如果linux就选linux

下载好工具照着生成就可以了

 (2) 把生成的公钥和私钥拷贝到trade/keys下面--->>>重命名--->>首位各添加下面的内容

  1. -----BEGIN PRIVATE KEY-----
  2. -----END PRIVATE KEY-----

(3)把支付宝公钥也拷贝到这路径下面,同样首尾添加

12.3.文档说明

我们主要用到电脑网站支付,文档地址:https://docs.open.alipay.com/270

用到的API接口:统一收单下单并支付页面接口

里面有文档说明

几个比较重要的参数

 sign签名

应该看未使用开放平台的SDK里面的说明:https://docs.open.alipay.com/291/106118

 请求参数

必填的几个:

12.4.编写代码

把环境改成本地的

首先的安装一个模块

  1. pip install pycryptodome

utils中新建alipay.py,代码如下:

  1. # apps/utils.py
  2.  
  3. import json
  4. from datetime import datetime
  5. from Crypto.PublicKey import RSA
  6. from Crypto.Signature import PKCS1_v1_5
  7. from Crypto.Hash import SHA256
  8. from base64 import b64encode, b64decode
  9. from urllib.parse import quote_plus
  10. from urllib.parse import urlparse, parse_qs
  11. from urllib.request import urlopen
  12. from base64 import decodebytes, encodebytes
  13.  
  14. class AliPay(object):
  15. """
  16. 支付宝支付接口
  17. """
  18. def __init__(self, appid, app_notify_url, app_private_key_path,
  19. alipay_public_key_path, return_url, debug=False):
  20. self.appid = appid
  21. self.app_notify_url = app_notify_url
  22. #私钥
  23. self.app_private_key_path = app_private_key_path
  24. self.app_private_key = None
  25. self.return_url = return_url
  26. with open(self.app_private_key_path) as fp:
  27. self.app_private_key = RSA.importKey(fp.read())
  28. #公钥
  29. self.alipay_public_key_path = alipay_public_key_path
  30. with open(self.alipay_public_key_path) as fp:
  31. self.alipay_public_key = RSA.import_key(fp.read())
  32.  
  33. if debug is True:
  34. self.__gateway = "https://openapi.alipaydev.com/gateway.do"
  35. else:
  36. self.__gateway = "https://openapi.alipay.com/gateway.do"
  37.  
  38. def direct_pay(self, subject, out_trade_no, total_amount, return_url=None, **kwargs):
  39. #请求参数
  40. biz_content = {
  41. "subject": subject,
  42. "out_trade_no": out_trade_no,
  43. "total_amount": total_amount,
  44. "product_code": "FAST_INSTANT_TRADE_PAY",
  45. # "qr_pay_mode":4
  46. }
  47. #允许传递更多参数,放到biz_content
  48. biz_content.update(kwargs)
  49. data = self.build_body("alipay.trade.page.pay", biz_content, self.return_url)
  50. return self.sign_data(data)
  51.  
  52. def build_body(self, method, biz_content, return_url=None):
  53. #build_body主要生产消息的格式
  54. #公共请求参数
  55. data = {
  56. "app_id": self.appid,
  57. "method": method,
  58. "charset": "utf-8",
  59. "sign_type": "RSA2",
  60. "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
  61. "version": "1.0",
  62. "biz_content": biz_content
  63. }
  64.  
  65. if return_url is not None:
  66. data["notify_url"] = self.app_notify_url
  67. data["return_url"] = self.return_url
  68.  
  69. return data
  70.  
  71. def sign_data(self, data):
  72. #签名
  73. data.pop("sign", None)
  74. # 排序后的字符串
  75. unsigned_items = self.ordered_data(data)
  76. #排完序后拼接起来
  77. unsigned_string = "&".join("{0}={1}".format(k, v) for k, v in unsigned_items)
  78. #这里得到签名的字符串
  79. sign = self.sign(unsigned_string.encode("utf-8"))
  80. #对url进行处理
  81. quoted_string = "&".join("{0}={1}".format(k, quote_plus(v)) for k, v in unsigned_items)
  82.  
  83. # 获得最终的订单信息字符串
  84. signed_string = quoted_string + "&sign=" + quote_plus(sign)
  85. return signed_string
  86.  
  87. #参数传进来一定要排序
  88. def ordered_data(self, data):
  89. complex_keys = []
  90. for key, value in data.items():
  91. if isinstance(value, dict):
  92. complex_keys.append(key)
  93.  
  94. # 将字典类型的数据dump出来
  95. for key in complex_keys:
  96. data[key] = json.dumps(data[key], separators=(',', ':'))
  97.  
  98. return sorted([(k, v) for k, v in data.items()])
  99.  
  100. def sign(self, unsigned_string):
  101. # 开始计算签名
  102. key = self.app_private_key
  103. #签名的对象
  104. signer = PKCS1_v1_5.new(key)
  105. #生成签名
  106. signature = signer.sign(SHA256.new(unsigned_string))
  107. # base64 编码,转换为unicode表示并移除回车
  108. sign = encodebytes(signature).decode("utf8").replace("\n", "")
  109. return sign
  110.  
  111. def _verify(self, raw_content, signature):
  112. # 开始计算签名
  113. key = self.alipay_public_key
  114. signer = PKCS1_v1_5.new(key)
  115. digest = SHA256.new()
  116. digest.update(raw_content.encode("utf8"))
  117. if signer.verify(digest, decodebytes(signature.encode("utf8"))):
  118. return True
  119. return False
  120.  
  121. def verify(self, data, signature):
  122. if "sign_type" in data:
  123. sign_type = data.pop("sign_type")
  124. # 排序后的字符串
  125. unsigned_items = self.ordered_data(data)
  126. message = "&".join(u"{}={}".format(k, v) for k, v in unsigned_items)
  127. return self._verify(message, signature)
  128.  
  129. if __name__ == "__main__":
  130. return_url = 'http://127.0.0.1:8000/?total_amount=100.00&timestamp=2017-08-15+23%3A53%3A34&sign=e9E9UE0AxR84NK8TP1CicX6aZL8VQj68ylugWGHnM79zA7BKTIuxxkf%2FvhdDYz4XOLzNf9pTJxTDt8tTAAx%2FfUAJln4WAeZbacf1Gp4IzodcqU%2FsIc4z93xlfIZ7OLBoWW0kpKQ8AdOxrWBMXZck%2F1cffy4Ya2dWOYM6Pcdpd94CLNRPlH6kFsMCJCbhqvyJTflxdpVQ9kpH%2B%2Fhpqrqvm678vLwM%2B29LgqsLq0lojFWLe5ZGS1iFBdKiQI6wZiisBff%2BdAKT9Wcao3XeBUGigzUmVyEoVIcWJBH0Q8KTwz6IRC0S74FtfDWTafplUHlL%2Fnf6j%2FQd1y6Wcr2A5Kl6BQ%3D%3D&trade_no=2017081521001004340200204115&sign_type=RSA2&auth_app_id=2016080600180695&charset=utf-8&seller_id=2088102170208070&method=alipay.trade.page.pay.return&app_id=2016080600180695&out_trade_no=20170202185&version=1.0'
  131. o = urlparse(return_url)
  132. query = parse_qs(o.query)
  133. processed_query = {}
  134. ali_sign = query.pop("sign")[0]
  135.  
  136. # 测试用例
  137. alipay = AliPay(
  138. # 沙箱里面的appid值
  139. appid="",
  140. #notify_url是异步的url
  141. app_notify_url="http://127.0.0.1:8000/",
  142. # 我们自己商户的密钥
  143. app_private_key_path="../trade/keys/private_2048.txt",
  144. # 支付宝的公钥
  145. alipay_public_key_path="../trade/keys/alipay_key_2048.txt", # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
  146. # debug为true时使用沙箱的url。如果不是用正式环境的url
  147. debug=True, # 默认False,
  148. return_url="http://127.0.0.1:8000/alipay/return/"
  149. )
  150.  
  151. for key, value in query.items():
  152. processed_query[key] = value[0]
  153. # print (alipay.verify(processed_query, ali_sign))
  154.  
  155. # 直接支付:生成请求的字符串。
  156. url = alipay.direct_pay(
  157. # 订单标题
  158. subject="测试订单derek",
  159. # 我们商户自行生成的订单号
  160. out_trade_no="20180417derek",
  161. # 订单金额
  162. total_amount=100,
  163. #成功付款后跳转到的页面,return_url同步的url
  164. # return_url="http://127.0.0.1:8000/"
  165. )
  166. # 将生成的请求字符串拿到我们的url中进行拼接
  167. re_url = "https://openapi.alipaydev.com/gateway.do?{data}".format(data=url)
  168.  
  169. print(re_url)

运行alipay.py,会生成一个支付链接url,点进去跳到支付页面

点“登录账户付款”,用沙箱账号付款

输入账号密码以及支付密码,成功付款

12.5.django集成支付宝notify_url和return_url

(1)配置url

  1. # 配置支付宝支付相关接口的url
  2. path('alipay/return/', AlipayView.as_view())

(2)alipay.py

把return_url和notify_url都改成远程服务器的地址

  1. return_url="http://47.93.198.159:8000/alipay/return/"
  2.  
  3. app_notify_url="http://47.93.198.159:8000/alipay/return/"
  1. # apps/utils.py
  2.  
  3. import json
  4. from datetime import datetime
  5. from Crypto.PublicKey import RSA
  6. from Crypto.Signature import PKCS1_v1_5
  7. from Crypto.Hash import SHA256
  8. from base64 import b64encode, b64decode
  9. from urllib.parse import quote_plus
  10. from urllib.parse import urlparse, parse_qs
  11. from urllib.request import urlopen
  12. from base64 import decodebytes, encodebytes
  13.  
  14. class AliPay(object):
  15. """
  16. 支付宝支付接口
  17. """
  18. def __init__(self, appid, app_notify_url, app_private_key_path,
  19. alipay_public_key_path, return_url, debug=False):
  20. self.appid = appid
  21. self.app_notify_url = app_notify_url
  22. #私钥
  23. self.app_private_key_path = app_private_key_path
  24. self.app_private_key = None
  25. self.return_url = return_url
  26. with open(self.app_private_key_path) as fp:
  27. self.app_private_key = RSA.importKey(fp.read())
  28. #公钥
  29. self.alipay_public_key_path = alipay_public_key_path
  30. with open(self.alipay_public_key_path) as fp:
  31. self.alipay_public_key = RSA.import_key(fp.read())
  32.  
  33. if debug is True:
  34. self.__gateway = "https://openapi.alipaydev.com/gateway.do"
  35. else:
  36. self.__gateway = "https://openapi.alipay.com/gateway.do"
  37.  
  38. def direct_pay(self, subject, out_trade_no, total_amount, return_url=None, **kwargs):
  39. #请求参数
  40. biz_content = {
  41. "subject": subject,
  42. "out_trade_no": out_trade_no,
  43. "total_amount": total_amount,
  44. "product_code": "FAST_INSTANT_TRADE_PAY",
  45. # "qr_pay_mode":4
  46. }
  47. #允许传递更多参数,放到biz_content
  48. biz_content.update(kwargs)
  49. data = self.build_body("alipay.trade.page.pay", biz_content, self.return_url)
  50. return self.sign_data(data)
  51.  
  52. def build_body(self, method, biz_content, return_url=None):
  53. #build_body主要生产消息的格式
  54. #公共请求参数
  55. data = {
  56. "app_id": self.appid,
  57. "method": method,
  58. "charset": "utf-8",
  59. "sign_type": "RSA2",
  60. "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
  61. "version": "1.0",
  62. "biz_content": biz_content
  63. }
  64.  
  65. if return_url is not None:
  66. data["notify_url"] = self.app_notify_url
  67. data["return_url"] = self.return_url
  68.  
  69. return data
  70.  
  71. def sign_data(self, data):
  72. #签名
  73. data.pop("sign", None)
  74. # 排序后的字符串
  75. unsigned_items = self.ordered_data(data)
  76. #排完序后拼接起来
  77. unsigned_string = "&".join("{0}={1}".format(k, v) for k, v in unsigned_items)
  78. #这里得到签名的字符串
  79. sign = self.sign(unsigned_string.encode("utf-8"))
  80. #对url进行处理
  81. quoted_string = "&".join("{0}={1}".format(k, quote_plus(v)) for k, v in unsigned_items)
  82.  
  83. # 获得最终的订单信息字符串
  84. signed_string = quoted_string + "&sign=" + quote_plus(sign)
  85. return signed_string
  86.  
  87. #参数传进来一定要排序
  88. def ordered_data(self, data):
  89. complex_keys = []
  90. for key, value in data.items():
  91. if isinstance(value, dict):
  92. complex_keys.append(key)
  93.  
  94. # 将字典类型的数据dump出来
  95. for key in complex_keys:
  96. data[key] = json.dumps(data[key], separators=(',', ':'))
  97.  
  98. return sorted([(k, v) for k, v in data.items()])
  99.  
  100. def sign(self, unsigned_string):
  101. # 开始计算签名
  102. key = self.app_private_key
  103. #签名的对象
  104. signer = PKCS1_v1_5.new(key)
  105. #生成签名
  106. signature = signer.sign(SHA256.new(unsigned_string))
  107. # base64 编码,转换为unicode表示并移除回车
  108. sign = encodebytes(signature).decode("utf8").replace("\n", "")
  109. return sign
  110.  
  111. def _verify(self, raw_content, signature):
  112. # 开始计算签名
  113. key = self.alipay_public_key
  114. signer = PKCS1_v1_5.new(key)
  115. digest = SHA256.new()
  116. digest.update(raw_content.encode("utf8"))
  117. if signer.verify(digest, decodebytes(signature.encode("utf8"))):
  118. return True
  119. return False
  120.  
  121. def verify(self, data, signature):
  122. if "sign_type" in data:
  123. sign_type = data.pop("sign_type")
  124. # 排序后的字符串
  125. unsigned_items = self.ordered_data(data)
  126. message = "&".join(u"{}={}".format(k, v) for k, v in unsigned_items)
  127. return self._verify(message, signature)
  128.  
  129. if __name__ == "__main__":
  130. return_url = 'http://127.0.0.1:8000/?total_amount=100.00&timestamp=2017-08-15+23%3A53%3A34&sign=e9E9UE0AxR84NK8TP1CicX6aZL8VQj68ylugWGHnM79zA7BKTIuxxkf%2FvhdDYz4XOLzNf9pTJxTDt8tTAAx%2FfUAJln4WAeZbacf1Gp4IzodcqU%2FsIc4z93xlfIZ7OLBoWW0kpKQ8AdOxrWBMXZck%2F1cffy4Ya2dWOYM6Pcdpd94CLNRPlH6kFsMCJCbhqvyJTflxdpVQ9kpH%2B%2Fhpqrqvm678vLwM%2B29LgqsLq0lojFWLe5ZGS1iFBdKiQI6wZiisBff%2BdAKT9Wcao3XeBUGigzUmVyEoVIcWJBH0Q8KTwz6IRC0S74FtfDWTafplUHlL%2Fnf6j%2FQd1y6Wcr2A5Kl6BQ%3D%3D&trade_no=2017081521001004340200204115&sign_type=RSA2&auth_app_id=2016080600180695&charset=utf-8&seller_id=2088102170208070&method=alipay.trade.page.pay.return&app_id=2016080600180695&out_trade_no=20170202185&version=1.0'
  131. o = urlparse(return_url)
  132. query = parse_qs(o.query)
  133. processed_query = {}
  134. ali_sign = query.pop("sign")[0]
  135.  
  136. # 测试用例
  137. alipay = AliPay(
  138. # 沙箱里面的appid值
  139. appid="",
  140. #notify_url是异步的url
  141. app_notify_url="http://47.93.198.159:8000/alipay/return/",
  142. # 我们自己商户的密钥
  143. app_private_key_path="../trade/keys/private_2048.txt",
  144. # 支付宝的公钥
  145. alipay_public_key_path="../trade/keys/alipay_key_2048.txt", # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
  146. # debug为true时使用沙箱的url。如果不是用正式环境的url
  147. debug=True, # 默认False,
  148. return_url="http://47.93.198.159:8000/alipay/return/"
  149. )
  150.  
  151. for key, value in query.items():
  152. processed_query[key] = value[0]
  153. # print (alipay.verify(processed_query, ali_sign))
  154.  
  155. # 直接支付:生成请求的字符串。
  156. url = alipay.direct_pay(
  157. # 订单标题
  158. subject="测试订单derek",
  159. # 我们商户自行生成的订单号
  160. out_trade_no="20180417derek2",
  161. # 订单金额
  162. total_amount=100,
  163. #成功付款后跳转到的页面,return_url同步的url
  164. return_url="http://47.93.198.159:8000/alipay/return/"
  165. )
  166. # 将生成的请求字符串拿到我们的url中进行拼接
  167. re_url = "https://openapi.alipaydev.com/gateway.do?{data}".format(data=url)
  168.  
  169. print(re_url)
  170.  
  171. alipay.py

alipay.py

(3)settings.py

settings中配置公钥私钥路径

  1. # 支付宝相关的key
  2. private_key_path = os.path.join(BASE_DIR, 'apps/trade/keys/private_2048.txt')
  3. ali_pub_key_path = os.path.join(BASE_DIR, 'apps/trade/keys/alipay_key_2048.txt')

(4)trade/views.py

  1. from datetime import datetime
  2. from utils.alipay import AliPay
  3. from rest_framework.views import APIView
  4. from MxShop.settings import ali_pub_key_path, private_key_path
  5. from rest_framework.response import Response
  6.  
  7. class AlipayView(APIView):
  8. def get(self, request):
  9. """
  10. 处理支付宝的return_url返回
  11. """
  12. processed_dict = {}
  13. # 1. 获取GET中参数
  14. for key, value in request.GET.items():
  15. processed_dict[key] = value
  16. # 2. 取出sign
  17. sign = processed_dict.pop("sign", None)
  18.  
  19. # 3. 生成ALipay对象
  20. alipay = AliPay(
  21. appid="2016091500517456",
  22. app_notify_url="http://47.93.198.159:8000/alipay/return/",
  23. app_private_key_path=private_key_path,
  24. alipay_public_key_path=ali_pub_key_path, # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
  25. debug=True, # 默认False,
  26. return_url="http://47.93.198.159:8000/alipay/return/"
  27. )
  28.  
  29. verify_re = alipay.verify(processed_dict, sign)
  30.  
  31. # 这里可以不做操作。因为不管发不发return url。notify url都会修改订单状态。
  32. if verify_re is True:
  33. order_sn = processed_dict.get('out_trade_no', None)
  34. trade_no = processed_dict.get('trade_no', None)
  35. trade_status = processed_dict.get('trade_status', None)
  36.  
  37. existed_orders = OrderInfo.objects.filter(order_sn=order_sn)
  38. for existed_order in existed_orders:
  39. existed_order.pay_status = trade_status
  40. existed_order.trade_no = trade_no
  41. existed_order.pay_time = datetime.now()
  42. existed_order.save()
  43.  
  44. def post(self, request):
  45. """
  46. 处理支付宝的notify_url
  47. """
  48. #存放post里面所有的数据
  49. processed_dict = {}
  50. #取出post里面的数据
  51. for key, value in request.POST.items():
  52. processed_dict[key] = value
  53. #把signpop掉,文档有说明
  54. sign = processed_dict.pop("sign", None)
  55.  
  56. #生成一个Alipay对象
  57. alipay = AliPay(
  58. appid="2016091500517456",
  59. app_notify_url="http://47.93.198.159:8000/alipay/return/",
  60. app_private_key_path=private_key_path,
  61. alipay_public_key_path=ali_pub_key_path, # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
  62. debug=True, # 默认False,
  63. return_url="http://47.93.198.159:8000/alipay/return/"
  64. )
  65.  
  66. #进行验证
  67. verify_re = alipay.verify(processed_dict, sign)
  68.  
  69. # 如果验签成功
  70. if verify_re is True:
  71. #商户网站唯一订单号
  72. order_sn = processed_dict.get('out_trade_no', None)
  73. #支付宝系统交易流水号
  74. trade_no = processed_dict.get('trade_no', None)
  75. #交易状态
  76. trade_status = processed_dict.get('trade_status', None)
  77.  
  78. # 查询数据库中订单记录
  79. existed_orders = OrderInfo.objects.filter(order_sn=order_sn)
  80. for existed_order in existed_orders:
  81. # 订单商品项
  82. order_goods = existed_order.goods.all()
  83. # 商品销量增加订单中数值
  84. for order_good in order_goods:
  85. goods = order_good.goods
  86. goods.sold_num += order_good.goods_num
  87. goods.save()
  88.  
  89. # 更新订单状态
  90. existed_order.pay_status = trade_status
  91. existed_order.trade_no = trade_no
  92. existed_order.pay_time = datetime.now()
  93. existed_order.save()
  94. #需要返回一个'success'给支付宝,如果不返回,支付宝会一直发送订单支付成功的消息
  95. return Response("success")

(5)trade/serializers.py

创建订单的时候生成一个支付的url,这个逻辑OderSerializer和OrderDetailSerializer中都添加

  1. #支付订单的url
  2. alipay_url = serializers.SerializerMethodField(read_only=True)
  3.  
  4. def get_alipay_url(self, obj):
  5. alipay = AliPay(
  6. appid="2016091500517456",
  7. app_notify_url="http://47.93.198.159:8000/alipay/return/",
  8. app_private_key_path=private_key_path,
  9. alipay_public_key_path=ali_pub_key_path, # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
  10. debug=True, # 默认False,
  11. return_url="http://47.93.198.159:8000/alipay/return/"
  12. )
  13.  
  14. url = alipay.direct_pay(
  15. subject=obj.order_sn,
  16. out_trade_no=obj.order_sn,
  17. total_amount=obj.order_mount,
  18. )
  19. re_url = "https://openapi.alipaydev.com/gateway.do?{data}".format(data=url)
  20.  
  21. return re_url
  1. # trade/serializer.py
  2. __author__ = 'derek'
  3.  
  4. import time
  5.  
  6. from .models import ShoppingCart
  7. from rest_framework import serializers
  8. from goods.models import Goods
  9. from goods.serializers import GoodsSerializer
  10. from .models import OrderInfo,OrderGoods
  11. from MxShop.settings import ali_pub_key_path, private_key_path
  12. from utils.alipay import AliPay
  13.  
  14. class ShopCartDetailSerializer(serializers.ModelSerializer):
  15. '''
  16. 购物车商品详情信息
  17. '''
  18. # 一个购物车对应一个商品
  19. goods = GoodsSerializer(many=False, read_only=True)
  20. class Meta:
  21. model = ShoppingCart
  22. fields = ("goods", "nums")
  23.  
  24. class ShopCartSerializer(serializers.Serializer):
  25. #获取当前登录的用户
  26. user = serializers.HiddenField(
  27. default=serializers.CurrentUserDefault()
  28. )
  29. nums = serializers.IntegerField(required=True, label="数量",min_value=1,
  30. error_messages={
  31. "min_value":"商品数量不能小于一",
  32. "required": "请选择购买数量"
  33. })
  34. #这里是继承Serializer,必须指定queryset对象,如果继承ModelSerializer则不需要指定
  35. #goods是一个外键,可以通过这方法获取goods object中所有的值
  36. goods = serializers.PrimaryKeyRelatedField(required=True, queryset=Goods.objects.all())
  37.  
  38. #继承的Serializer没有save功能,必须写一个create方法
  39. def create(self, validated_data):
  40. # validated_data是已经处理过的数据
  41. #获取当前用户
  42. # view中:self.request.user;serizlizer中:self.context["request"].user
  43. user = self.context["request"].user
  44. nums = validated_data["nums"]
  45. goods = validated_data["goods"]
  46.  
  47. existed = ShoppingCart.objects.filter(user=user, goods=goods)
  48. #如果购物车中有记录,数量+1
  49. #如果购物车车没有记录,就创建
  50. if existed:
  51. existed = existed[0]
  52. existed.nums += nums
  53. existed.save()
  54. else:
  55. #添加到购物车
  56. existed = ShoppingCart.objects.create(**validated_data)
  57.  
  58. return existed
  59.  
  60. def update(self, instance, validated_data):
  61. # 修改商品数量
  62. instance.nums = validated_data["nums"]
  63. instance.save()
  64. return instance
  65.  
  66. #订单中的商品
  67. class OrderGoodsSerialzier(serializers.ModelSerializer):
  68. goods = GoodsSerializer(many=False)
  69. class Meta:
  70. model = OrderGoods
  71. fields = "__all__"
  72.  
  73. #订单商品信息
  74. # goods字段需要嵌套一个OrderGoodsSerializer
  75. class OrderDetailSerializer(serializers.ModelSerializer):
  76. goods = OrderGoodsSerialzier(many=True)
  77. # 支付订单的url
  78. alipay_url = serializers.SerializerMethodField(read_only=True)
  79.  
  80. def get_alipay_url(self, obj):
  81. alipay = AliPay(
  82. appid="",
  83. app_notify_url="http://47.93.198.159:8000/alipay/return/",
  84. app_private_key_path=private_key_path,
  85. alipay_public_key_path=ali_pub_key_path, # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
  86. debug=True, # 默认False,
  87. return_url="http://47.93.198.159:8000/alipay/return/"
  88. )
  89.  
  90. url = alipay.direct_pay(
  91. subject=obj.order_sn,
  92. out_trade_no=obj.order_sn,
  93. total_amount=obj.order_mount,
  94. )
  95. re_url = "https://openapi.alipaydev.com/gateway.do?{data}".format(data=url)
  96.  
  97. return re_url
  98. class Meta:
  99. model = OrderInfo
  100. fields = "__all__"
  101.  
  102. class OrderSerializer(serializers.ModelSerializer):
  103. user = serializers.HiddenField(
  104. default=serializers.CurrentUserDefault()
  105. )
  106. #生成订单的时候这些不用post
  107. pay_status = serializers.CharField(read_only=True)
  108. trade_no = serializers.CharField(read_only=True)
  109. order_sn = serializers.CharField(read_only=True)
  110. pay_time = serializers.DateTimeField(read_only=True)
  111. nonce_str = serializers.CharField(read_only=True)
  112. pay_type = serializers.CharField(read_only=True)
  113. #支付订单的url
  114. alipay_url = serializers.SerializerMethodField(read_only=True)
  115.  
  116. def get_alipay_url(self, obj):
  117. alipay = AliPay(
  118. appid="",
  119. app_notify_url="http://47.93.198.159:8000/alipay/return/",
  120. app_private_key_path=private_key_path,
  121. alipay_public_key_path=ali_pub_key_path, # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
  122. debug=True, # 默认False,
  123. return_url="http://47.93.198.159:8000/alipay/return/"
  124. )
  125.  
  126. url = alipay.direct_pay(
  127. subject=obj.order_sn,
  128. out_trade_no=obj.order_sn,
  129. total_amount=obj.order_mount,
  130. )
  131. re_url = "https://openapi.alipaydev.com/gateway.do?{data}".format(data=url)
  132.  
  133. return re_url
  134.  
  135. def generate_order_sn(self):
  136. #生成订单号
  137. # 当前时间+userid+随机数
  138. from random import Random
  139. random_ins = Random()
  140. order_sn = "{time_str}{userid}{ranstr}".format(time_str=time.strftime("%Y%m%d%H%M%S"),
  141. userid=self.context["request"].user.id,
  142. ranstr=random_ins.randint(10, 99))
  143. return order_sn
  144.  
  145. def validate(self, attrs):
  146. #validate中添加order_sn,然后在view中就可以save
  147. attrs["order_sn"] = self.generate_order_sn()
  148. return attrs
  149.  
  150. class Meta:
  151. model = OrderInfo
  152. fields = "__all__"
  153.  
  154. trade/serializer.py

trade/serializer.py

(6)测试代码

把本地修改的地方一定要上传到服务器,因为我们需要在服务器上调试代码

vue项目中api.js里面local_host改为服务器ip

  1. let local_host = 'http://47.93.198.159:8000';

在phcharm中开始运行项目

浏览器访问地址:http://47.93.198.159:8000/orders/

创建一个订单

生成订单的详情

12.6.vue静态文件放到django中

vue有两种开发模式

  • build    用来生成静态文件
  • dev

(1)运行

  1. cnpm run build

生成的静态文件在dist目录下面

(2)把index.html拷贝到templates目录下

(3)django中创建static目录

  • 把index.entry.js考到django的static目录下面
  • 把dist/static下的两个文件夹拷贝到django static目录下

(4)settings设置静态文件路径

  1. STATIC_URL = '/static/'
  2. STATICFILES_DIRS = (
  3. os.path.join(BASE_DIR, "static"),
  4. )

(5)修改index.html中静态文件路径

  1. <script type="text/javascript" src="/static/index.entry.js"></script></body>

(6)配置index的url

  1. from django.views.generic import TemplateView
  2.  
  3. urlpatterns = [
  4. # 首页
  5. path('index/', TemplateView.as_view(template_name='index.html'),name='index')
  6. ]

(7)配置支付成功return的地址

trade/views.py

  1. response = redirect("/index/#/app/home/member/order")
    return response
    else:
    response = redirect("index")
    return response
  1. # trade/views.py
  2.  
  3. from rest_framework import viewsets
  4. from rest_framework.permissions import IsAuthenticated
  5. from utils.permissions import IsOwnerOrReadOnly
  6. from rest_framework_jwt.authentication import JSONWebTokenAuthentication
  7. from rest_framework.authentication import SessionAuthentication
  8. from .serializers import ShopCartSerializer,ShopCartDetailSerializer,OrderDetailSerializer,OrderGoodsSerialzier,OrderSerializer
  9. from .models import ShoppingCart,OrderGoods,OrderInfo
  10. from rest_framework import mixins
  11. from django.shortcuts import render, redirect
  12.  
  13. class ShoppingCartViewset(viewsets.ModelViewSet):
  14. """
  15. 购物车功能
  16. list:
  17. 获取购物车详情
  18. create:
  19. 加入购物车
  20. delete:
  21. 删除购物记录
  22. """
  23. permission_classes = (IsAuthenticated, IsOwnerOrReadOnly)
  24. authentication_classes = (JSONWebTokenAuthentication, SessionAuthentication)
  25. serializer_class = ShopCartSerializer
  26. #商品的id
  27. lookup_field = "goods_id"
  28.  
  29. def get_serializer_class(self):
  30. if self.action == 'list':
  31. return ShopCartDetailSerializer
  32. else:
  33. return ShopCartSerializer
  34.  
  35. #获取购物车列表
  36. def get_queryset(self):
  37. return ShoppingCart.objects.filter(user=self.request.user)
  38.  
  39. class OrderViewset(mixins.ListModelMixin, mixins.RetrieveModelMixin, mixins.CreateModelMixin, mixins.DestroyModelMixin,
  40. viewsets.GenericViewSet):
  41. """
  42. 订单管理
  43. list:
  44. 获取个人订单
  45. delete:
  46. 删除订单
  47. create:
  48. 新增订单
  49. """
  50. permission_classes = (IsAuthenticated, IsOwnerOrReadOnly)
  51. authentication_classes = (JSONWebTokenAuthentication, SessionAuthentication)
  52. serializer_class = OrderSerializer
  53. #动态配置serializer
  54. def get_serializer_class(self):
  55. if self.action == "retrieve":
  56. return OrderDetailSerializer
  57. return OrderSerializer
  58. #获取订单列表
  59. def get_queryset(self):
  60. return OrderInfo.objects.filter(user=self.request.user)
  61.  
  62. #在订单提交保存之前还需要多两步步骤,所以这里自定义perform_create方法
  63. #1.将购物车中的商品保存到OrderGoods中
  64. #2.情况购物车
  65. def perform_create(self, serializer):
  66. order = serializer.save()
  67. # 获取购物车所有商品
  68. shop_carts = ShoppingCart.objects.filter(user=self.request.user)
  69. for shop_cart in shop_carts:
  70. order_goods = OrderGoods()
  71. order_goods.goods = shop_cart.goods
  72. order_goods.goods_num = shop_cart.nums
  73. order_goods.order = order
  74. order_goods.save()
  75. #清空购物车
  76. shop_cart.delete()
  77. return order
  78.  
  79. from datetime import datetime
  80. from utils.alipay import AliPay
  81. from rest_framework.views import APIView
  82. from MxShop.settings import ali_pub_key_path, private_key_path
  83. from rest_framework.response import Response
  84.  
  85. class AlipayView(APIView):
  86. def get(self, request):
  87. """
  88. 处理支付宝的return_url返回
  89. """
  90. processed_dict = {}
  91. # 1. 获取GET中参数
  92. for key, value in request.GET.items():
  93. processed_dict[key] = value
  94. # 2. 取出sign
  95. sign = processed_dict.pop("sign", None)
  96.  
  97. # 3. 生成ALipay对象
  98. alipay = AliPay(
  99. appid="",
  100. app_notify_url="http://47.93.198.159:8000/alipay/return/",
  101. app_private_key_path=private_key_path,
  102. alipay_public_key_path=ali_pub_key_path, # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
  103. debug=True, # 默认False,
  104. return_url="http://47.93.198.159:8000/alipay/return/"
  105. )
  106.  
  107. verify_re = alipay.verify(processed_dict, sign)
  108.  
  109. # 这里可以不做操作。因为不管发不发return url。notify url都会修改订单状态。
  110. if verify_re is True:
  111. order_sn = processed_dict.get('out_trade_no', None)
  112. trade_no = processed_dict.get('trade_no', None)
  113. trade_status = processed_dict.get('trade_status', None)
  114.  
  115. existed_orders = OrderInfo.objects.filter(order_sn=order_sn)
  116. for existed_order in existed_orders:
  117. existed_order.pay_status = trade_status
  118. existed_order.trade_no = trade_no
  119. existed_order.pay_time = datetime.now()
  120. existed_order.save()
  121.  
  122. response = redirect("/index/#/app/home/member/order")
  123. return response
  124.  
  125. else:
  126. response = redirect("index")
  127. return response
  128.  
  129. def post(self, request):
  130. """
  131. 处理支付宝的notify_url
  132. """
  133. #存放post里面所有的数据
  134. processed_dict = {}
  135. #取出post里面的数据
  136. for key, value in request.POST.items():
  137. processed_dict[key] = value
  138. #把signpop掉,文档有说明
  139. sign = processed_dict.pop("sign", None)
  140.  
  141. #生成一个Alipay对象
  142. alipay = AliPay(
  143. appid="",
  144. app_notify_url="http://47.93.198.159:8000/alipay/return/",
  145. app_private_key_path=private_key_path,
  146. alipay_public_key_path=ali_pub_key_path, # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
  147. debug=True, # 默认False,
  148. return_url="http://47.93.198.159:8000/alipay/return/"
  149. )
  150.  
  151. #进行验证
  152. verify_re = alipay.verify(processed_dict, sign)
  153.  
  154. # 如果验签成功
  155. if verify_re is True:
  156. #商户网站唯一订单号
  157. order_sn = processed_dict.get('out_trade_no', None)
  158. #支付宝系统交易流水号
  159. trade_no = processed_dict.get('trade_no', None)
  160. #交易状态
  161. trade_status = processed_dict.get('trade_status', None)
  162.  
  163. # 查询数据库中订单记录
  164. existed_orders = OrderInfo.objects.filter(order_sn=order_sn)
  165. for existed_order in existed_orders:
  166. # 订单商品项
  167. order_goods = existed_order.goods.all()
  168. # 商品销量增加订单中数值
  169. for order_good in order_goods:
  170. goods = order_good.goods
  171. goods.sold_num += order_good.goods_num
  172. goods.save()
  173.  
  174. # 更新订单状态
  175. existed_order.pay_status = trade_status
  176. existed_order.trade_no = trade_no
  177. existed_order.pay_time = datetime.now()
  178. existed_order.save()
  179. #需要返回一个'success'给支付宝,如果不返回,支付宝会一直发送订单支付成功的消息
  180. return Response("success")
  181.  
  182. # trade/views.py

trade/views.py

现在可以通过index直接访问了:http://47.93.198.159:8000/index/#/app/home/index

添加商品到购物车-->>去结算-->>生成订单-->>跳到支付页面了

Django REST framework+Vue 打造生鲜电商项目(笔记九)的更多相关文章

  1. Django REST framework+Vue 打造生鲜电商项目(笔记二)

    (转自https://www.cnblogs.com/derek1184405959/p/8768059.html)(有修改) 接下来开始引入django resfulframework,体现它的强大 ...

  2. Django REST framework+Vue 打造生鲜电商项目(笔记四)

    (PS:部分代码和图片来自博客:http://www.cnblogs.com/derek1184405959/p/8813641.html.有增删) 一.用户登录和手机注册 1.drf的token功能 ...

  3. Django REST framework+Vue 打造生鲜电商项目(笔记十)

    (from:https://www.cnblogs.com/derek1184405959/p/8877643.html  有修改) 十三.首页.商品数量.缓存和限速功能开发 首先把pycharm环境 ...

  4. Django REST framework+Vue 打造生鲜电商项目(笔记三)

    (PS:转载自http://www.cnblogs.com/derek1184405959/p/8810591.html  有修改) 一.drf的过滤 (1)添加到app里面 INSTALLED_AP ...

  5. Django REST framework+Vue 打造生鲜电商项目(笔记十一)

    (form: http://www.cnblogs.com/derek1184405959/p/8886796.html 有修改) 十四.social_django 集成第三方登录 1.申请应用 进入 ...

  6. Django REST framework+Vue 打造生鲜电商项目(笔记八)

    (form:http://www.cnblogs.com/derek1184405959/p/8862569.html) 十一.pycharm 远程代码调试 第三方登录和支付,都需要有服务器才行(回调 ...

  7. Django REST framework+Vue 打造生鲜电商项目(笔记七)

    十.购物车.订单管理和支付功能 1.添加商品到购物车 (1)trade/serializer.py 这里的serializer不继承ModelSerializer,是因为自己写的Serializer更 ...

  8. Django REST framework+Vue 打造生鲜电商项目(笔记一)

    首先,这系列随笔是我个人在学习Bobby老师的Django实战项目中,记录的觉得对自己来说比较重要的知识点,不是完完整整的项目步骤过程....如果有小伙伴想找完整的教程,可以看看这个(https:// ...

  9. Django REST framework+Vue 打造生鲜电商项目(笔记六)

    (部分代码来自https://www.cnblogs.com/derek1184405959/p/8836205.html) 九.个人中心功能开发 1.drf的api文档自动生成 (1) url #d ...

随机推荐

  1. miniconda3 安装tensorflow

    使用miniconda3进行安装 conda create -n tensorflow conda install tensorflow 输入下面的代码进行测试 import tensorflow a ...

  2. git的快速入门

    Git是目前世界上最先进的分布式版本控制系统(注意,仅仅是一个程序,而不是正真意义上的系统). Why为什么需要版本控制? 场景1:大学毕业前夕,你在完成毕业论文,初稿A写好了,找老师修改,老师提出意 ...

  3. 考试应对(Java语法速览)

    1.从命令行输入数据 格式:Scanner reader=new Scanner(System.in); 此reader对象可以使用的方法:nextBoolean(),nextByte(),nextS ...

  4. 基于TCP的编程

    前提:本文基于Linux系统下的学习 服务器端 1 创建通讯端口,返回socket设备的文件描述符 sfdsocket(2)#include <sys/types.h> /* See NO ...

  5. Go语言学习笔记(8)——包和结构体

    包 —— 每个可执行的应用程序必须包含一个主函数,它是执行的入口点.主函数应该存在main包中. 结构体: 通过 . 操作符访问结构体的各个成员! 1. 定义结构体类型person: type per ...

  6. ALV报表——点击事件(二)

    目录 一.ALV点击事件(双击) 一.ALV点击事件(双击) 代码: *Report ZRFI001_XFL_TEST REPORT ZRFI001_XFL_TEST . *定义ALV所需要用到的类型 ...

  7. 网页调试js时,如何知道某个事件对应哪段js代码?

    有时候我们需要知道某个事件对应的js代码,比如点击一个div元素时,出现下拉框,我想知道这个功能对应的js代码,那就可以按下图操作: 勾选click事件,重新运行,那么就会在每个click事件那里设置 ...

  8. sql 语句实现一串数字位数不足在左侧补0的技巧

    https://www.cnblogs.com/mylydg/p/5725189.html 在日常使用sql做查询插入操作时,我们通常会用到用sql查询一串编号,这串编号由数字组成.为了统一美观,我们 ...

  9. Asp.net Core 2.0 OpenId Connect Handler缺失Claims?

    原文:https://leastprivilege.com/2017/11/15/missing-claims-in-the-asp-net-core-2-openid-connect-handler ...

  10. 使用Feign通过服务名调用服务,找不到服务

    fegineureka 报错环境: eureka注册中心在远程服务器上 本地服务注册到远程的eureka注册中心 本地服务通过Fegin组件+服务名调用服务 报错时,注册中心的情况: Applicat ...