👏👏👏
哈喽!大家好,我是【太阳打伞】,一位热爱分享各种技术的博主!😍😍😍
⭐【太阳打伞】的创作宗旨:每一条命令都亲自执行过,每一行代码都实际运行过,每一种方法都真实实践过,每一篇文章都良心制作过。✊✊✊
⭐【太阳打伞】的博客中所有涉及命令、代码的地方,除了提供图片供大家参考,另外会在图片下方提供一份纯文本格式的命令或者代码方便大家粘贴复制直接执行命令或者运行代码。🤝🤝🤝
⭐如果你对技术有着浓厚的兴趣,欢迎关注【太阳打伞】,欢迎大家和我一起交流。😘😘😘
❤️❤️❤️感谢各位朋友接下来的阅读❤️❤️❤️
流程图:
!!!:效果内容在最下方,一定要看呀。
第一步:
登录支付宝,打开控制台,进入研发服务
第二步:
进入沙箱,生成自己的私钥,支付宝公钥
将支付宝公钥和自己私钥存放到.txt文件中
第三步:
拿到自己的支付宝沙箱应用id,和 买家账号:xxxxxxxxxxxx
登录密码111111
支付密码111111,
买家账号等到支付时使用!
第四步:
后端代码实现:
创建alipay.py文件
我采用的是Tornado框架实现的!!!
from datetime import datetime
from Crypto.PublicKey import RSA
from Crypto.Signature import PKCS1_v1_5
from Crypto.Hash import SHA256
from urllib.parse import quote_plus
from urllib.parse import urlparse, parse_qs
from base64 import decodebytes, encodebytes
import json
from tornado import httpclient
class AliPay:
"""
支付宝支付接口(PC端支付接口)
"""
def __init__(self, appid, app_notify_url, app_private_key_path,
alipay_public_key_path, return_url, debug=True):
self.appid = appid
self.app_notify_url = app_notify_url
self.app_private_key_path = app_private_key_path
self.app_private_key = None
self.return_url = return_url
with open(self.app_private_key_path) as fp:
self.app_private_key = RSA.importKey(fp.read())
self.alipay_public_key_path = alipay_public_key_path
with open(self.alipay_public_key_path) as fp:
self.alipay_public_key = RSA.importKey(fp.read())
if debug is True:
self.__gateway = "https://openapi.alipaydev.com/gateway.do"
else:
self.__gateway = "https://openapi.alipay.com/gateway.do"
# 商户订单号out_trade_no
def direct_pay(self, subject, out_trade_no, total_amount, return_url=None, **kwargs):
biz_content = {
"subject": subject,
"out_trade_no": out_trade_no,
"total_amount": total_amount,
"product_code": "FAST_INSTANT_TRADE_PAY",
# "qr_pay_mode":4
}
biz_content.update(kwargs)
data = self.build_body("alipay.trade.page.pay", biz_content, self.return_url)
return self.sign_data(data)
# 查询订单状态方法
async def api_alipay_trade_query(self,out_trade_no=None,**kwargs):
biz_content = {
"out_trade_no": out_trade_no
}
biz_content.update(**kwargs)
data = self.build_body("alipay.trade.query", biz_content)
url = self.__gateway + "?" + self.sign_data(data)
res = await httpclient.AsyncHTTPClient().fetch(url,method='GET',validate_cert=False,connect_timeout=30.0, request_timeout=30.0)
res = json.loads(res.body.decode())
return res
#请求支付宝退款接口
async def api_alipay_trade_refund(self, refund_amount, out_trade_no=None, trade_no=None, **kwargs):
biz_content = {
"refund_amount": refund_amount
}
biz_content.update(**kwargs)
if out_trade_no:
biz_content["out_trade_no"] = out_trade_no
if trade_no:
biz_content["trade_no"] = trade_no
data = self.build_body("alipay.trade.refund", biz_content)
url = self.__gateway + "?" + self.sign_data(data)
res = await httpclient.AsyncHTTPClient().fetch(url,method='GET',validate_cert=False,connect_timeout=30.0, request_timeout=30.0)
res = json.loads(res.body.decode())
return res
def build_body(self, method, biz_content, return_url=None):
data = {
"app_id": self.appid,
"method": method,
"charset": "utf-8",
"sign_type": "RSA2",
"timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
"version": "1.0",
"biz_content": biz_content
}
if return_url is not None:
data["notify_url"] = self.app_notify_url
data["return_url"] = self.return_url
return data
def sign_data(self, data):
data.pop("sign", None)
# 排序后的字符串
unsigned_items = self.ordered_data(data)
unsigned_string = "&".join("{0}={1}".format(k, v) for k, v in unsigned_items)
sign = self.sign(unsigned_string.encode("utf-8"))
# ordered_items = self.ordered_data(data)
quoted_string = "&".join("{0}={1}".format(k, quote_plus(v)) for k, v in unsigned_items)
# 获得最终的订单信息字符串
signed_string = quoted_string + "&sign=" + quote_plus(sign)
return signed_string
def ordered_data(self, data):
complex_keys = []
for key, value in data.items():
if isinstance(value, dict):
complex_keys.append(key)
# 将字典类型的数据dump出来
for key in complex_keys:
data[key] = json.dumps(data[key], separators=(',', ':'))
return sorted([(k, v) for k, v in data.items()])
def sign(self, unsigned_string):
# 开始计算签名
key = self.app_private_key
signer = PKCS1_v1_5.new(key)
signature = signer.sign(SHA256.new(unsigned_string))
# base64 编码,转换为unicode表示并移除回车
sign = encodebytes(signature).decode("utf8").replace("\n", "")
return sign
def _verify(self, raw_content, signature):
# 开始计算签名
key = self.alipay_public_key
signer = PKCS1_v1_5.new(key)
digest = SHA256.new()
digest.update(raw_content.encode("utf8"))
if signer.verify(digest, decodebytes(signature.encode("utf8"))):
return True
return False
def verify(self, data, signature):
if "sign_type" in data:
sign_type = data.pop("sign_type")
# 排序后的字符串
unsigned_items = self.ordered_data(data)
message = "&".join(u"{}={}".format(k, v) for k, v in unsigned_items)
return self._verify(message, signature)
第五步:
写pay.py主文件:
import datetime
import logging
import decimal
from base import BaseHandler
from tornado.web import url
from db import UserModel,OrderModel
from user import BaseMange
# 充值
from alipay import AliPay
from utils import jwt_auth
# 钱包
class WalletHandler(BaseMange):
"""创建订单"""
@jwt_auth
async def post(self):
_where = "model.uid == {}".format(self._cuser.id)
orders = await self.get_all_where(OrderModel,_where)
return self.finish({"errcode":0,"data":orders})
@jwt_auth
async def get(self):
# 查询用户余额
user = await self.get_one_obj(UserModel, self._cuser.id)
return self.finish({'errcode': 0, 'data': user.wallet})
class PayHandler(BaseMange):
# 转跳地址
@jwt_auth
async def get(self):
alipay = AliPay(
appid='xxxxxxxxxx', # 你的支付宝沙箱应用的id
app_private_key_path='./keys/app_private_2048.txt', # 你的私钥
alipay_public_key_path='./keys/alipay_public_2048.txt', # 支付宝公钥,
app_notify_url="http://127.0.0.1:8080/wallet", # 回调地址
return_url='http://127.0.0.1:8080/pay',
debug=True # 请求来到支付宝沙箱)
)
# 3.创建订单
order_id = datetime.datetime.now().strftime("%Y%m%d%H%M%S") + "_%s" % self._cuser.id
subject = self.get_argument("subject",None)
# 订单编号
total_amount= self.get_argument("total_amount",None)
# res = alipay.direct_pay(subject,order_id,total_amount)
# print(res)
# pay_url = "https://openapi.alipaydev.com/gateway.do?"+res # 支付宝网关地址(沙箱应用)
# return self.finish({'errcode': 0, 'data': pay_url})
"""创建订单"""
try:
# 创建订单信息
order = {"order_id": order_id, "uid_id": self._cuser.id, "total_amount": float(total_amount)}
order = await self.create(OrderModel, order)
res = alipay.direct_pay(subject, order_id, total_amount)
print(res)
pay_url = "https://openapi.alipaydev.com/gateway.do?" + res # 支付宝网关地址(沙箱应用)
# print(pay_url)
return self.finish({'errcode': 0, 'data': pay_url})
except Exception as e:
logging.error("create order error, reason is %s" % e)
return self.finish({'errcode': 1, 'msg': "交易出错!"})
@jwt_auth
async def post(self):
order_id = self.get_argument("out_trade_no", None) # 订单编号
_where = "(model.order_id=='{}')".format(order_id)
alipay = AliPay(
appid='xxxxxxxxxx', # 你的支付宝沙箱应用的id
app_private_key_path='./keys/app_private_2048.txt', # 你的私钥
alipay_public_key_path='./keys/alipay_public_2048.txt', # 支付宝公钥,
app_notify_url="http://127.0.0.1:8080/wallet", # 回调地址
return_url='http://127.0.0.1:8080/pay',
debug=True # 请求来到支付宝沙箱)
)
res = await alipay.api_alipay_trade_query(out_trade_no=order_id)
print(res)
res = res['alipay_trade_query_response']
if res['code'] == '10000':
try:
order = await self.get_one_where(OrderModel, _where)
# 修改订单的状态
order.trade_no = res['trade_no']
order.total_amount = res['total_amount']
order.status = 1
order.record = res["send_pay_date"]
await self.application.objects.update(order)
user = await self.get_one_obj(UserModel, self._cuser.id)
user.wallet = user.wallet + decimal.Decimal(res['total_amount'])
await self.application.objects.update(user)
return self.finish({'errcode': 0, 'msg': "充值成功"})
except Exception as e:
print(e)
return self.finish({'errcode': 1, 'msg': "未找到订单交易!"})
else:
return self.finish({'errcode': 2, 'msg': "支付失败!"})
urlpatterns = [
url('/pay/', PayHandler),
url('/wallet/', WalletHandler),
]
第六步:
前端代码实现
我使用的是Vue.3
文件名是:wallet.vue
<template>
<div class="container">
<a-layout>
<a-layout-sider>
<mymenu></mymenu>
</a-layout-sider>
<a-layout>
<h2>充值页面</h2>
<h3>我的钱包</h3>
<p>
<a-col :span="22">
<a-statistic title="钱包余额" :precision="2" :value= wallet />
</a-col>
</p>
<div class="components-input-demo-presuffix">
<h2>钱包充值</h2>
<p>请输入充值金额:
<a-input v-model:value="value" prefix="¥" suffix="RMB" placeholder="请输入充值金额" />
</p>
<p>
<a-button type="primary" @click="get_pay()">支付宝充值</a-button> 
<a-button type="">微信充值</a-button>
</p>
</div>
<p>用户订单表</p>
<p><table>
<tr>
<th>订单编号</th>
<th>创建时间</th>
<th>支付方式</th>
<th>备注</th>
<th>支付状态</th>
</tr>
<tr v-for="(j,i) in orderlist" :key="i">
<td>{{j.order_id}}</td>
<td>{{j.create_time}}</td>
<td>{{j.pay_method}}</td>
<td>{{j.record}}</td>
<td v-if="j.status==0">待支付</td>
<td v-if="j.status==1">已支付</td>
<td v-if="j.status==3">已取消</td>
</tr>
</table></p>
<!-- <a-button type="link" @click="pay">开始充值</a-button>
<a-modal v-model:visible="visible" title="充值" @ok="visible=false">
充值金额:<a-input v-model:value="money" />
<a-button type="link" @click="pay">开始充值</a-button>
</a-modal> -->
</a-layout>
</a-layout>
</div>
</template>
<script>
// 导入子组件
import mymenu from "./mymenu.vue";
import { message } from "ant-design-vue";
export default {
data() {
return {
collapsed: false,
selectedKeys: ["1"],
visible:false,
money:'',
id:'',
wallet:0,
value:50,
orderlist:[]
};
},
//声明组件
components: {
mymenu,
},
methods: {
pay(id){
this.visible = true
// this.id = id
},
// 充值请求
get_pay() {
this.myaxios(this.baseurl + "pay/", "get",{"subject":"钱包充值","total_amount":this.value}).then(data => {
console.log(data);
if (data.errcode === 0) {
window.location.href=data.data
}
});
},
get_wallet() {
this.myaxios(this.baseurl + "wallet/", "get").then(data => {
console.log(data);
if (data.errcode === 0) {
this.wallet = data.data
}
});
},
get_orderlist(){
this.myaxios(this.baseurl + "wallet/", "post").then(data => {
console.log(data);
if (data.errcode === 0) {
this.orderlist = data.data
}
});
}
},
created() {
this.get_wallet()
this.get_orderlist()
},
mounted() {},
};
</script>
<style scoped>
@import url("../assets/style.css");
</style>
充值请求:
文件名是:pay.vue
<template>
<div class="container">
<a-layout>
<a-layout-sider>
<mymenu></mymenu>
</a-layout-sider>
<a-layout>
<h2>充值页面</h2>
</a-layout>
</a-layout>
</div>
</template>
<script>
// 导入子组件
import mymenu from "./mymenu.vue";
import { message } from "ant-design-vue";
import { createFromIconfontCN } from '@ant-design/icons-vue';
const IconFont = createFromIconfontCN({
scriptUrl: '//at.alicdn.com/t/font_8d5l8fzk5b87iudi.js',
});
export default {
data() {
return {
collapsed: false,
selectedKeys: ["1"],
};
},
//声明组件
components: {
mymenu,
},
methods: {
// 充值请求
check_order(){
this.myaxios(this.baseurl + "pay/", "post",{"out_trade_no":this.$route.query.out_trade_no,"trade_no":this.$route.query.trade_no,"total_amount":this.$route.query.total_amount,"timestamp":this.$route.query.timestamp}).then(data => {
console.log(data);
if (data.errcode === 0) {
this.$message.success(data.msg)
window.location.href="/wallet"
}else{
this.$message.error(data.msg)
window.location.href="/wallet"
}
});
}
},
created() {
this.check_order()
},
mounted() {},
};
</script>
<style scoped>
@import url("../assets/style.css");
</style>
第七步:
数据库内容,字段名
订单表:
用户表:记得写钱包字段哦
效果内容(show):
点击支付宝充值:
输入从沙箱获得的支付宝账号和密码。
点击确认付款!
后面还让输入支付密码!!!
钱包到账啦:
这个就是支付宝支付,代码有效,修改即用!!!