工厂模式我们在之前已经使用过了 当时我们做的是三方登录 有兴趣的可以戳这里
所需包
from rest_framework.views import APIView
from mydjango.settings import alipay
from mydjango import settings
from payment.models import *
from payment.wx_pay import get_sign, trans_dict_to_xml,
trans_xml_to_dict
from rest_framework.response import Response
import json
import random
import requests
from user.models import User
import uuid
import qrcode
import paypalrestsdk
支付宝
大家先去看官方文档
或者看我的教程
settings.py配置
# 支付宝配置
from alipay import AliPay
# 回调地址
RETURN_URL = 'http://127.0.0.1:8000/pay/ali_back/'
app_private_key_string = open('{}/apps/payment/alipay_key/app_private_key'.format(BASE_DIR)).read()
alipay_public_key_string = open('{}/apps/payment/alipay_key/alipay_public'.format(BASE_DIR)).read()
alipay = AliPay(
appid="2016102500759596",
app_notify_url=None, # 默认回调url
app_private_key_string=app_private_key_string,
# 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
alipay_public_key_string=alipay_public_key_string,
sign_type="RSA2", # RSA 或者 RSA2
debug=True, # 默认False
)
支付接口
我们在调用支付接口时就要生成一个订单信息
支付宝生成一个支付地址 跳转即可支付
class Ali(APIView):
def post(self,request):
subject = "余额充值"
# 电脑网站支付,需要跳转到https://openapi.alipay.com/gateway.do? + order_string
order_string = alipay.api_alipay_trade_page_pay(
# 订单号
out_trade_no="唯一不重复字符串",
# 金额
total_amount="金额",
subject='subject',
# 回调地址
return_url=settings.RETURN_URL,
# notify_url="http://localhost:8000/pay/ali_back/" # 可选, 不填则使用默认notify url
)
pay_url = 'https://openapi.alipaydev.com/gateway.do?' + order_string
return Response({'data': {'pay_url': pay_url, 'msg': 'OK', "code": 200}})
回调地址
我们根据回调信息对订单表进行修改 例如支付状态修改
class AliBack(APIView):
def get(self,request):
data = request.data
data.pop("sign")
print(data)
return Response({"code": 200, "msg": 'ok'})
路由配置
from django.urls import path, include
from .views import *
urlpatterns = [
path('ali_pay/', Ali.as_view()), # 支付页面
path('ali_back/', AliBack.as_view()), # 回调地址
]
微信扫码支付
大家先去看官方文档
或者看[教程
支付接口!!!
大家这里需要注意 我们生成的二维码为真实有效的 并不是虚拟货币 需要谨慎操作
大家这里需要注意 我们生成的二维码为真实有效的 并不是虚拟货币 需要谨慎操作
大家这里需要注意 我们生成的二维码为真实有效的 并不是虚拟货币 需要谨慎操作
wx_pay.py
我们在调用支付接口时就要生成一个订单信息
微信支付生成二维码
views.py
生成图片在项目文件下
from .wx_pay import get_sign, trans_dict_to_xml, trans_xml_to_dict
class Wx(APIView):
def post(self, request):
url = 'https://api.mch.weixin.qq.com/pay/unifiedorder' # 微信扫码支付接口
key = '945bec9df3614cffb74e39aba8fbf7d7' #
total_fee = 1 # 支付金额,单位分
body = '123123' # 商品描述
out_trade_no = 'order_%s' % random.randrange(100000, 999999) # 订单编号
params = {
'appid': 'wx092344a76b9979ff', # APPID
'mch_id': '1602932608', # 商户号
'notify_url': 'http://wxpay.v3u.cn/wx_back/', # 回调地址
'product_id': 'goods_%s' % random.randrange(100000, 999999), # 商品编号
'trade_type': 'NATIVE', # 支付类型(扫码支付)
'spbill_create_ip': '114.254.176.137', # 发送请求服务器的IP地址
'total_fee': total_fee, # 订单总金额
'out_trade_no': out_trade_no, # 订单编号
'body': body, # 商品描述
'nonce_str': 'ibuaiVcKdpRxkhJA' # 字符串
}
sign = get_sign(params, key) # 获取签名
params.setdefault('sign', sign) # 添加签名到参数字典
xml = trans_dict_to_xml(params) # 转换字典为XML
response = requests.request('post', url, data=xml) # 以POST方式向微信公众平台服务器发起请求
data_dict = trans_xml_to_dict(response.content) # 将请求返回的数据转为字典
print(data_dict)
qrcode_name = out_trade_no + '.png' # 支付二维码图片保存路径
if data_dict.get('return_code') == 'SUCCESS': # 如果请求成功
img = qrcode.make(data_dict.get('code_url')) # 创建支付二维码片
img.save('./' + qrcode_name) # 保存支付二维码
return Response({'img': qrcode_name})
验证支付地址
微信需要我们去验证一下是否完成
class WxBack(APIView):
def get(self, request):
# 统一订单查询接口
url = "https://api.mch.weixin.qq.com/pay/orderquery"
out_trade_no = "order_235117" # 支付后的商户订单号
key = '945bec9df3614cffb74e39aba8fbf7d7' # 商户api密钥
params = {
'appid': 'wx092344a76b9979ff', # APPID
'mch_id': '1602932608', # 商户号
'out_trade_no': out_trade_no, # 订单编号
'nonce_str': 'ibuaiVcKdpRxkhJA' # 随机字符串
}
sign = get_sign(params, key) # 获取签名
params.setdefault('sign', sign) # 添加签名到参数字典
xml = trans_dict_to_xml(params) # 转换字典为XML
response = requests.request('post', url, data=xml) # 以POST方式向微信公众平台服务器发起请求
data_dict = trans_xml_to_dict(response.content) # 将请求返回的数据转为字典
print(data_dict)
# 支付成功 订单未支付
if data_dict.get("trade_state_desc") == "支付成功":
pass
return Response({"msg": 'ok'})
路由配置
from django.urls import path, include
from .views import *
urlpatterns = [
path('wx_pay/', Wx.as_view()), # 支付页面
path('wx_back/', WxBack.as_view()), # 回调地址
]
PayPal 国际支付
支付接口
我们在调用支付接口时就要生成一个订单信息
class PayPal(APIView):
def get(self, request):
paypalrestsdk.configure({
"mode": "sandbox", # sandbox代表沙盒
"client_id": "AYXXU0zjJVvQMIEzG6NPLauHuiMJSAGN8wpOnu1T7gNCm4JMc4Nw7QcZSgKhSbHyUSHKnhZqQ-lsEm8l",
"client_secret": "ECa2PqKtQ-dD2NYO0eunVG4pXPNWCakssPuufcaT0ZtpdTTxx0G4nza78NM2D1w0w8jduWsmL98nA6rq"
})
payment = paypalrestsdk.Payment({
"intent": "sale",
"payer": {
"payment_method": "paypal"},
"redirect_urls": {
"return_url": "http://localhost:8000/pay/pay_back/", # 支付成功跳转页面
"cancel_url": "http://localhost:3000/pay/pay_back/"}, # 取消支付页面
"transactions": [{
# "item_list": {
# "items": [{
# "name": str(obj.order_id),
# }]},
"amount": {
"total": 5.00,
"currency": "USD"},
"description": "这是一个订单测试"}]})
if payment.create():
for link in payment.links:
if link.rel == "approval_url":
approval_url = str(link.href)
print("Redirect for approval: %s" % (approval_url))
return Response({"url": approval_url})
else:
print(payment.error)
return Response({"msg": "支付失败"})
回调地址
我们根据回调信息对订单表进行修改 例如支付状态修改
class PayBack(APIView):
def get(self,request):
# ?paymentId=PAYID-L7UHJGA6ED88532FU3155144&token=EC-6AR335876U336883C&PayerID=73S3E9HVESDX4
paymentid = request.GET.get("paymentId") # 订单id
payerid = request.GET.get("PayerID") # 支付者id
print(payerid)
print(paymentid)
payment = paypalrestsdk.Payment.find(paymentid)
if payment.execute({"payer_id": payerid}):
payment_history = paypalrestsdk.Payment.all({"count": 10})
print(payment_history.payments)
return "支付成功"
else:
print(payment.error) # Error Hash
return "支付失败"
路由配置
from django.urls import path, include
from .views import *
urlpatterns = [
path('pay_pay/', PayPal.as_view()), # 支付页面
path('pay_back/', PayBack.as_view()), # 回调地址
]
工厂模式
这时候我们发现三方支付的流程大致是一样的
我们进行资源整合
models.py
# Create your models here.
from django.db import models
from user.models import User
from utils.basemodel import Base
# User有用户名即可 username
# 充值
class Recharge(models.Model):
PAY_METHOD = (
(1, "支付宝"),
(2, "微信"),
(3, "Pay"),
)
ORDER_STATUS = (
(1, "待充值"),
(2, "已充值"),
(3, "已取消"),
)
user = models.ForeignKey(User, on_delete=models.PROTECT, verbose_name="充值用户")
order_id = models.CharField('订单号', max_length=124)
trade_no = models.CharField('支付宝订单号', max_length=132, null=True) # 28位
pay_time = models.DateTimeField('充值时间', null=True)
pay_method = models.CharField('充值方式', choices=PAY_METHOD, default=1, max_length=8)
status = models.CharField('充值状态', choices=ORDER_STATUS, default=1, max_length=8)
total_amount = models.DecimalField(max_digits=10, decimal_places=2, verbose_name="充值金额")
class Meta:
db_table = '充值支付'
def __str__(self):
return self.order_id
plant.py 工厂模式
import json
import random
import requests
from payment.models import *
from payment.wx_pay import get_sign, trans_dict_to_xml, trans_xml_to_dict
from user.models import User
import uuid
from mydjango.settings import alipay
from mydjango import settings
import qrcode
import paypalrestsdk
class WxPay:
def __repr__(self, request):
# 统一订单查询接口
url = "https://api.mch.weixin.qq.com/pay/orderquery"
out_trade_no = str(request.GET.get("img")[0:-4]) # 支付后的商户订单号
key = '945bec9df3614cffb74e39aba8fbf7d7' # 商户api密钥
params = {
'appid': 'wx092344a76b9979ff', # APPID
'mch_id': '1602932608', # 商户号
'out_trade_no': out_trade_no, # 订单编号
'nonce_str': 'ibuaiVcKdpRxkhJA' # 随机字符串
}
sign = get_sign(params, key) # 获取签名
params.setdefault('sign', sign) # 添加签名到参数字典
xml = trans_dict_to_xml(params) # 转换字典为XML
response = requests.request('post', url, data=xml) # 以POST方式向微信公众平台服务器发起请求
data_dict = trans_xml_to_dict(response.content) # 将请求返回的数据转为字典
# print(data_dict)
user_obj = User.objects.filter(username=request.user.username)
# 支付成功 订单未支付
if data_dict.get("trade_state_desc") == "支付成功" and Recharge.objects.filter(
order_id=data_dict.get("out_trade_no")).first().status == '1':
Recharge.objects.filter(order_id=data_dict.get("out_trade_no")).update(status=2)
user_obj.update(balance=user_obj.first().balance + int(data_dict.get("total_fee")))
return 'ok'
def pay(self, request):
url = 'https://api.mch.weixin.qq.com/pay/unifiedorder' # 微信扫码支付接口
key = '945bec9df3614cffb74e39aba8fbf7d7' #
total_fee = 1 # 支付金额,单位分
body = '123' # 商品描述
out_trade_no = 'order_%s' % random.randrange(100000, 999999) # 订单编号
user = request.user
pay_method = request.data.get("pay_method")
total_amount = request.data.get("total_amount")
data = {
"user": user,
"order_id": out_trade_no,
"pay_method": int(pay_method),
"total_amount": total_amount,
}
obj = Recharge.objects.create(**data)
obj.save()
params = {
'appid': 'wx092344a76b9979ff', # APPID
'mch_id': '1602932608', # 商户号
'notify_url': 'http://wxpay.v3u.cn/wx_back/', # 回调地址
'product_id': 'goods_%s' % random.randrange(100000, 999999), # 商品编号
'trade_type': 'NATIVE', # 支付类型(扫码支付)
'spbill_create_ip': '114.254.176.137', # 发送请求服务器的IP地址
'total_fee': total_fee, # 订单总金额
'out_trade_no': out_trade_no, # 订单编号
'body': body, # 商品描述
'nonce_str': 'ibuaiVcKdpRxkhJA' # 字符串
}
sign = get_sign(params, key) # 获取签名
params.setdefault('sign', sign) # 添加签名到参数字典
xml = trans_dict_to_xml(params) # 转换字典为XML
response = requests.request('post', url, data=xml) # 以POST方式向微信公众平台服务器发起请求
data_dict = trans_xml_to_dict(response.content) # 将请求返回的数据转为字典
# print(data_dict)
qrcode_name = out_trade_no + '.png' # 支付二维码图片保存路径
if data_dict.get('return_code') == 'SUCCESS': # 如果请求成功
img = qrcode.make(data_dict.get('code_url')) # 创建支付二维码片
img.save('static/we_pay/' + qrcode_name) # 保存支付二维码
return {'img': qrcode_name}
# return HttpResponse(json.dumps({'img': qrcode_name}))
class PayPal:
def __repr__(self, request):
# ?paymentId=PAYID-L7UHJGA6ED88532FU3155144&token=EC-6AR335876U336883C&PayerID=73S3E9HVESDX4
paymentid = request.GET.get("paymentId") # 订单id
payerid = request.GET.get("PayerID") # 支付者id
print(payerid)
print(paymentid)
payment = paypalrestsdk.Payment.find(paymentid)
if payment.execute({"payer_id": payerid}):
payment_history = paypalrestsdk.Payment.all({"count": 10})
print(payment_history.payments)
# Recharge.objects.filter(order_id=data_dict.get("out_trade_no")).update(status=2)
# user_obj = User.objects.filter(username=request.user.username)
# user_obj.update(balance=user_obj.first().balance + int(data_dict.get("total_fee")))
return "支付成功"
else:
print(payment.error) # Error Hash
return "支付失败"
def pay(self, request):
user = request.user
pay_method = request.data.get("pay_method")
total_amount = request.data.get("total_amount")
total_amount = total_amount / 6.531
total_amount = round(total_amount, 2)
data = {
"user": user,
"order_id": uuid.uuid4(),
"pay_method": int(pay_method),
"total_amount": total_amount,
}
obj = Recharge.objects.create(**data)
obj.save()
paypalrestsdk.configure({
"mode": "sandbox", # sandbox代表沙盒
"client_id": "AYXXU0zjJVvQMIEzG6NPLauHuiMJSAGN8wpOnu1T7gNCm4JMc4Nw7QcZSgKhSbHyUSHKnhZqQ-lsEm8l",
"client_secret": "ECa2PqKtQ-dD2NYO0eunVG4pXPNWCakssPuufcaT0ZtpdTTxx0G4nza78NM2D1w0w8jduWsmL98nA6rq"
})
payment = paypalrestsdk.Payment({
"intent": "sale",
"payer": {
"payment_method": "paypal"},
"redirect_urls": {
"return_url": "http://localhost:8000/pay/pay_back/", # 支付成功跳转页面
"cancel_url": "http://localhost:3000/pay/pay_back/"}, # 取消支付页面
"transactions": [{
# "item_list": {
# "items": [{
# "name": str(obj.order_id),
# }]},
"amount": {
"total": total_amount,
"currency": "USD"},
"description": "这是一个订单测试"}]})
if payment.create():
for link in payment.links:
if link.rel == "approval_url":
approval_url = str(link.href)
print("Redirect for approval: %s" % (approval_url))
return {"url": approval_url}
else:
print(payment.error)
return "支付失败"
class AliPay:
def __repr__(self, request):
data = request.data
data.pop("sign")
print(data)
# orders_obj = Recharge.objects.get(order_id=data['out_trade_no'])
# orders_obj.trade_no = data['trade_no']
# orders_obj.status = 2
# orders_obj.save()
return {"code": 200, "msg": 'ok'}
def pay(self, request):
user = request.user
pay_method = request.data.get("pay_method")
total_amount = request.data.get("total_amount")
data = {
"user": user,
"order_id": str(uuid.uuid4()),
"pay_method": int(pay_method),
"total_amount": total_amount,
}
obj = Recharge.objects.create(**data)
subject = "余额充值"
# 电脑网站支付,需要跳转到https://openapi.alipay.com/gateway.do? + order_string
order_string = alipay.api_alipay_trade_page_pay(
# 订单号
out_trade_no=obj.order_id,
# 金额
total_amount=obj.total_amount,
subject='subject',
# 回调地址
return_url=settings.RETURN_URL,
# notify_url="http://localhost:8000/pay/ali_back/" # 可选, 不填则使用默认notify url
)
obj.save()
pay_url = 'https://openapi.alipaydev.com/gateway.do?' + order_string
return {'data': {'pay_url': pay_url, 'msg': 'OK', "code": 200}}
# 工厂类
class Plant:
@staticmethod
def product_pay(why, request):
# print(why)
if why == 1:
return AliPay()
elif why == 2:
return WxPay()
elif why == 3:
return PayPal()
if why == "ali_back":
a = AliPay()
return a.__repr__(request)
elif why == "wx_back":
w = WxPay()
return w.__repr__(request)
elif why == "pay_back":
p = PayPal()
return p.__repr__(request)
views.py 视图
# 普通充值
class PlantAPIView(APIView):
def post(self, request):
balance = request.data.get("balance")
user_obj = User.objects.filter(pk=request.user.id)
try:
list2 = r6.zrange("ranking", 0, 10, withscores=True)
list2.reverse()
list3 = []
for i in list2:
list3.append({i[0].decode(): i[1]})
for x in list3:
for k, v in x.items():
if k == request.user.username:
total = int(balance) + int(v)
r6.zadd("ranking", {str(request.user.username): total})
except:
r6.zadd("ranking", {str(request.user.username): int(balance)})
user_obj.update(balance=user_obj.first().balance + balance)
return Response({"msg": "ok"})
# 工厂模式 支付
class PlantPayAPIView(APIView):
permission_classes = (AllowAny,)
# 充值地址或者二维码
def post(self, request):
why = request.data.get("pay_method")
pay_url = plant.Plant.product_pay(why, request)
return Response(pay_url.pay(request))
# 回调地址 获取用户详细信息
def get(self, request):
details = plant.Plant.product_pay(request.path_info.split('/')[2], request)
return Response({"msg": "ok", "details": details})
# 获取余额
class BalanceAPIView(APIView):
def get(self, request):
return Response({"balance": request.user.balance})
urls.py 路由
from django.urls import path, include
from .views import *
urlpatterns = [
# path('balance/', balance),
path('balance/', BalanceAPIView.as_view()), # 获取余额
path('pay/', PlantPayAPIView.as_view()), # 工厂模式 支付
path('ali_back/', PlantPayAPIView.as_view()), # 阿里回调地址
path('wx_back/', PlantPayAPIView.as_view()), # wx 验证地址
path('pay_back/', PlantPayAPIView.as_view()), # pal回调地址
path('plant/', PlantAPIView.as_view()), # 普通充值接口
]
vue
wallet.vue 前端页面展示
<template>
<div>
<a-layout id="components-layout-demo-custom-trigger">
<a-layout-sider :trigger="null" collapsible>
<div class="logo"/>
<leftmenu :menu_number='2'/>
</a-layout-sider>
<div>
<h1>我的钱包---余额 {{ balance }}</h1>
<div>
<a-button type="primary" @click="() => (modal2Visible = true)">
充值
</a-button>
<a-modal
v-model="modal2Visible"
title="Vertically centered modal dialog"
centered
@ok="() => (modal2Visible = false)"
>
充值金额
<a-input-number
:formatter="value => `$ ${value}`.replace(/\B(?=(\d{3})+(?!\d))/g, ',')"
:parser="value => value.replace(/\$\s?|(,*)/g, '')"
@change="onChange"
/>
<button @click="postPlant">普通充值</button>
<br>
<br>
<br>
<br>
选择支付
<img src="../images/django-vue支付工厂模式之微信、支付宝、PalPay.assets/a055c8aD09.png" alt="" @click="postBalance(1)">
<img src="../images/django-vue支付工厂模式之微信、支付宝、PalPay.assets/3Da0d6Df8E.png" alt="" @click="postBalance(2)">
<img src="../images/django-vue支付工厂模式之微信、支付宝、PalPay.assets/149EcEfAe3.png" alt="" @click="postBalance(3)">
<br>
<img :src="'http://127.0.0.1:8000/static/we_pay/' + we_img" alt="" v-if="we_img" title="微信扫码">
</a-modal>
</div>
<br>
<br>
<div>
<h1>我的优惠券</h1>
<p></p>
</div>
</div>
</a-layout>
</div>
</template>
<script>
//导入组件
import leftmenu from './leftmenu.vue';
import staff_service from './staff_service.vue';
//导入跨域
import {getBalance, getWxBack, postPlant, postRecharge, postWxBack} from "../http/apis";
import md5 from "js-md5";
export default {
data() {
return {
username: localStorage.getItem("username"),
balance: 0,
modal2Visible: false,
total_amount: 0,
we_img: '',
message: [],
}
},
//注册组件标签
components: {
'staff_service': staff_service,
'leftmenu': leftmenu,
},
methods: {
postPlant() {
let data = {
balance: this.total_amount,
}
postPlant(data).then(res => {
console.log(res)
this.getBalance()
this.modal2Visible = false
})
},
onChange(value) {
if (value >= 1) {
this.total_amount = value
}
},
//获取余额
getBalance() {
getBalance().then(res => {
// console.log(res)
this.balance = res.balance
})
},
//跳转支付
postBalance(value) {
console.log('postBalance', value);
let data = {
total_amount: this.total_amount,
pay_method: value
}
postRecharge(data).then(res => {
console.log(res)
// this.$router.push(res.data.pay_url)
if (value === 1) {
window.open(res.data.pay_url)
} else if (value === 2) {
this.we_img = res.img
} else if (value === 3) {
window.open(res.url)
}
})
},
},
created() {
this.getBalance()
},
}
</script>
<style scoped>
#components-layout-demo-custom-trigger .trigger {
font-size: 18px;
line-height: 64px;
padding: 0 24px;
cursor: pointer;
transition: color 0.3s;
}
#components-layout-demo-custom-trigger .trigger:hover {
color: #1890ff;
}
#components-layout-demo-custom-trigger .logo {
height: 32px;
background: rgba(255, 255, 255, 0.2);
margin: 16px;
}
</style>
apis.js 访问接口
//获取用户余额
export const getBalance = (params, headers) => get("/pay/balance/", params, headers)
// 充值接口 三方的
export const postRecharge = (params, headers) => post("/pay/pay/", params, headers)
// 充值接口 自定义的
export const postPlant = (params, headers) => post("/pay/plant/", params, headers)
// 查看微信支付是否完成
export const getWxBack = (params, headers) => get("/pay/wx_back/", params, headers)
index.js 配置路由
{path: '/wallet', name: "余额", component: wallet},
总结
总体来说是没有难度的 大家要细心 注意配置文件以及秘钥 这里就全体跑通服务了
大家需要多看官方文档
支付宝沙箱是不稳定的,大家需要注意
微信我们这里使用的是二维码支付,大家也可以使用别的进行支付,没有回调地址,我们支付完成后要进行验证
paypal是国际通用的 大家要进行实时汇率转换 我这里是写死的 注意
我们这里还可以进行拓展 例如充值排行榜和优惠券 稍后带来
觉得文章写的不错,可以请喝杯咖啡
- Post link: https://yanxiang.wang/django-vue%E6%94%AF%E4%BB%98%E5%B7%A5%E5%8E%82%E6%A8%A1%E5%BC%8F%E4%B9%8B%E5%BE%AE%E4%BF%A1%E3%80%81%E6%94%AF%E4%BB%98%E5%AE%9D%E3%80%81PalPay/
- Copyright Notice: All articles in this blog are licensed under unless otherwise stated.