[DEMO]支付宝网页支付及微信网页扫码支付
- 格式:docx
- 大小:384.71 KB
- 文档页数:9
34-⽀付宝⽀付⽀付宝⽀付⼀、快速连接通道1. ⽀付宝<1>. ⽀付宝API:六⼤接⼝<2>. ⽀付宝⼯作流程<3>. ⽀付宝8次异步通知机制(⽀付宝对我们的服务器发送POST请求,索要success7个字符)2. 沙箱环境<1>. 在沙箱环境下实名认证<2>. 电脑⽹站⽀付API<3>. 完成RSA秘钥⽣成<4>. 在开发中⼼的沙箱应⽤下设置应⽤公钥填⼊⽣成的公钥⽂件中的内容<5>. Python⽀付宝开源框架pip install python-alipay-sdk --upgrade<6>. 公钥私钥设置"""# alipay_public_key.pem-----BEGIN PUBLIC KEY-----⽀付宝公钥-----END PUBLIC KEY-----# app_private_key.pem-----BEGIN RSA PRIVATE KEY-----⽤户私钥-----END RSA PRIVATE KEY-----"""<7>. ⽀付宝回调连接⼆、⽀付流程图三、⽀付宝介⼊⼊门1. 流程'''# ⽀付宝开放平台1. 服务范围(⾃研开发服务) -> 实名认证2. 控制台 -> 我的应⽤ -> 创建应⽤ -> ⽹页&移动应⽤ -> ⽀付接⼊ -> 应⽤名称 -> 应⽤图标 ->1) 移动应⽤ -> 应⽤平台 -> Bundle ID ...2) ⽹页应⽤ (不成功. 需要使⽤营业执照) -> ⽹址url -> 简介注意: 先选择功能再审核能⼒列表:添加能⼒ -> ⽀付能⼒ -> 电脑⽹站⽀付 开发设置:加签管理 -> 公钥 -⽀付宝⽹关应⽤⽹关授权回调地址3. ⽂档 -> ⽹页 & 移动应⽤接⼝⽂档能⼒列表1) 开放能⼒:⽀付能⼒ -> 电脑⽹站⽀付2) 产品介绍:注意: 会跳到⽀付宝的页⾯, ⽀付宝会有⼀个get页⾯回调, post数据返回后端回调费率: 0.6%3) 快速接⼊:SDK快速接⼊: python没有, 只能使⽤API开发⽀付流程: 下单 -> 商户系统 -> ⽀付宝 -> 回调(get显⽰订单结果, post修改订单状态)4) ⽀付API:公共请求参数请求参数订单号 out_trade_no总⾦额 total_amount订单标题 subjet公共响应参数⽀付宝交易号 trade_no我们的订单号 out_trade_no5) GitHub开源SDKpip install python-alipay-sdk# ⽀付宝沙箱环境1. 沙箱环境地址: https:///platform/appDaily.htm2. 沙箱应⽤:APPID⽀付宝⽹关: 地址中带dev表⽰沙箱环境, 不带表⽰正式环境加密⽅式: 使⽤⽀付宝提供的密钥⽣成(⽀付宝开放平台组助⼿).之前是xx.jar包, 现在变成xx.exe软件. 需要⽣成公钥和私钥将⾃⼰的公钥配置在⽀付宝中, ⽀付宝会⽣成⼀个⽀付宝的公钥.3. 项⽬中使⽤:注释 .read这⾥是操作⽂件的app_private_key_string 配置⾃⼰的私钥alipay_public_key_string 配置⽀付宝的公钥注意: 不能有空格AliPay类中的参数配置:APPID配置沙箱环境的APPIDsign_type 配置⾃⼰的 RSA2debug=False测试环境, True正式环境alipay.api_alipay_trade_page_pay中的参数配置:return_url 回调地址 (注意: 需要使⽤公⽹地址)notify_url 回调地址⽀付宝⽹关 + order_string => ⽣成连接地址提⽰: ⽣成连接地址打开会出现钓鱼⽹站异常4. 解决提⽰钓鱼问题: 浏览器⾥⾯有多个窗⼝沙箱环境存在的问题, 如果出现问题, 开⽆痕窗⼝即可, 付完之后会回调到之前配置的return_url中配置的⽹页⽀付宝沙箱环境充值:控制台 -> 沙箱账号 -> 账户余额# ⽀付宝公私密钥⽣成, sdk使⽤⽀付宝开放平台组助⼿使⽤: ⽣成公私钥⽀付宝开放平台下载:https:///ide/getPluginUrl.htm?clientType=assistant&platform=win&channelType=WEB密钥长度: RSA2密钥格式: PKCS1⽣成即可GitHub开源SDK:⽀付宝开源框架地址: https:///fzlee/alipaypip install python-alipay-sdk# 拓展:xx.apk 如果apk使⽤QQ 或者微信传送, 它会改名, 再后⾯加个.1 -> xx.apk.1. ⽬的就是防⽌恶意软件.如果你需要安装, 只需要将后缀名修改过来即可'''2. 测试⽬录结构3. t_alipay.pyfrom alipay import AliPayapp_private_key_string = """-----BEGIN rsa2 PRIVATE KEY-----MIIEowIBAAKCAQEAr6my/KRUtoPcQzuBt8TZtxLvLtwI8Rf/ETubH6dfi143yuiHd0SnfTctD+ZTmGyRHxuqNwwTNV4CN0d58wuI2F3hky4Tm8ocp8n0tzjlYxDvoh1b4d4ksxXCM0yhSzywdIK+K+Y9VP74uU4mlT47oBFUs6TBK9AAlMfZfoPTUAUjSDF -----END rsa2 PRIVATE KEY-----"""alipay_public_key_string = """-----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCqAQEAgvXw19HTUH0t1thzkoq8KBhDBwFCoDqRJyBYnpN/KOTxTuSoUR0+pLK3vJbeQ0w5GJ/tiHpLh38hc88LNSR5nk26IBXX8WuNmxxC56d/A4/AaqiO3xgs9jKZjvYs0xuaFkwLswMuD8vm3 -----END PUBLIC KEY-----"""alipay = AliPay(appid="2021000117620642",app_notify_url=None, # 默认回调urlapp_private_key_string=app_private_key_string,# ⽀付宝的公钥,验证⽀付宝回传消息使⽤,不是你⾃⼰的公钥,alipay_public_key_string=alipay_public_key_string,sign_type="RSA2", # rsa2 或者 RSA2debug=False # 默认False)# 如果你是 Python 3的⽤户,使⽤默认的字符串即可subject = "测试订单"# 电脑⽹站⽀付,需要跳转到https:///gateway.do? + order_stringalipay_url = 'https:///gateway.do?'order_string = alipay.api_alipay_trade_page_pay(out_trade_no="20161112", # 订单号, 必须唯⼀total_amount=10, # 总⾦额subject=subject, # 订单标题return_url="http://139.196.184.91/", # 同步回调(⽀付成功)notify_url="http://139.196.184.91/" # 异步回调(订单状态) 可选, 不填则使⽤默认notify url)print(alipay_url + order_string)4. 注意事项from alipay import AliPayapp_private_key_string = """-----BEGIN rsa2 PRIVATE KEY-----MIIEowIBAAKCAQEAr6my/KRUtoPcQzuBt8TZtxLvLtwI8Rf/ETubH6dfi143yuiHd0SnfTctD+ZTmGyRHxuqNwwTNV4CN0d58wuI2F3hky4Tm8ocp8n0tzjlYxDvoh1b4d4ksxXCM0yhSzywdIK+K+Y9VP74uU4mlT47oBFUs6TBK9AAlMfZfoPTUAUjSDF -----END rsa2 PRIVATE KEY-----"""alipay_public_key_string = """-----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAgvXw19HTUH0t1thzkoq8KBhDBwFCoDqRJyBYnpN/KOTxTuSoUR0+pLK3vJbeQ0w5GJ/tiHpLh38hc88LNSR5nk26IBXX8WuNmxxC56d/A4/AaqiO3xgs9jKZjvYs0xuaFkwLswMuD8vm3x -----END PUBLIC KEY-----"""alipay = AliPay(appid="2021000117620642",app_notify_url=None, # 默认回调urlapp_private_key_string=app_private_key_string,# ⽀付宝的公钥,验证⽀付宝回传消息使⽤,不是你⾃⼰的公钥,alipay_public_key_string=alipay_public_key_string,sign_type="RSA2", # rsa2 或者 RSA2debug=False # 默认False)# 如果你是 Python 3的⽤户,使⽤默认的字符串即可subject = "测试订单"# 电脑⽹站⽀付,需要跳转到https:///gateway.do? + order_stringalipay_url = 'https:///gateway.do?'order_string = alipay.api_alipay_trade_page_pay(out_trade_no="20161112", # 订单号, 必须唯⼀total_amount=10, # 总⾦额subject=subject, # 订单标题return_url="http://139.196.184.91/", # 同步回调(⽀付成功)notify_url="http://139.196.184.91/" # 异步回调(订单状态) 可选, 不填则使⽤默认notify url)print(alipay_url + order_string)四、⽀付宝⼆次封装1. GitHub开源框架参考https:///fzlee/alipay2. 调⽤⽀付宝⽀付SDKpip install python-alipay-sdk --upgrade3. 流程'''1. libs中新建⽂件, ⽂件中新建__init__.py, 新建.py⽂件2. 将之前写死的 app...string 等, 修改成从⽂件中读取 open().read()3. 新建⽂件夹存放⽀付宝公钥和⾃⼰的私钥⽤于被第⼆步读取公钥私钥存放的⽂件格式是:-----xxx-----公钥或者私钥-----xxx-----4. 新建settings.py⽂件存放⼀些常量5. debug 配置成和 setting.py中的debug⼀直性6. 使⽤三元运算配置⽀付宝的⽀付⽹关7. 使⽤__init__.py优化导⼊的层级注意: ⽹站⽀付alipay.api_alipay_trade_page_pay放到外⾯书写和订单⼀起.'''4. ⽬录结构libs├── al_alipay # aliapy⼆次封装包│├── __init__.py # 包⽂件│├── pem # 公钥私钥⽂件夹││├── alipay_public_key.pem # ⽀付宝公钥⽂件││├── app_private_key.pem # 应⽤私钥⽂件│├── pay.py # ⽀付⽂件└──└── settings.py # 应⽤配置5. pem/alipay_public_key.pem-----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAt99Bp0XLP1Zu2WdRu74CMB/tVx1/2thIo8t3oAo8eD8smku1e76PfeOw4iqYMHU32Vq1Fg7BLa9oPMw7Ro+kNjX4jTDz4wC3LA6dUI5OeGxYd9+tkpsBwyg+buVNhhogppQn5rcCzkRFTx0D -----END PUBLIC KEY-----6. pem/app_private_key.pem-----BEGIN RSA PRIVATE KEY-----MIIEowIBAAKCAQEAj91mUtyrPlFFkfoLB+66lYcwexzXzEt6SlJuxsj3lW6+8pqla4YKqiUf98DeuBpX+USFm+baYFPqP5FWKyAUmGSDU8T4xD9BwLc+gm7rjeEjE5LzdyMInoEjW0QKXnn6S5y4gGPwI2WjOhg9vfr2R0GTDMTqn4i7zDB/u+wTksX5e -----END RSA PRIVATE KEY-----7. __init__.pyfrom .alipay_task import alipay, alipay_gateway8. pay.pyfrom alipay import AliPayfrom . import settingsalipay = AliPay(appid=settings.APPID,app_notify_url=None,app_private_key_string=settings.APP_PRIVATE_KEY_SIRING,alipay_public_key_string=settings.ALIPAY_PUBLIC_KEY_SIRING,sign_type=settings.SIGN_TYPE,debug=settings.DEBUG,)gateway = settings.GATEWAY9. settings.pyimport osAPPID = '2021000117613064'# 默认回调APP_NOTIFY_URL = None# ⾃⼰私钥APP_PRIVATE_KEY_SIRING = open(os.path.join(os.path.dirname(__file__), 'pem', 'app_private_kay.pem')).read()# 阿⾥公钥ALIPAY_PUBLIC_KEY_SIRING = open(os.path.join(os.path.dirname(__file__), 'pem', 'alipay_public_key.pem')).read()# 标签加密类型SIGN_TYPE = 'RSA2'# True表⽰测试沙箱环境DEBUG = True# 阿⾥⽹关GATEWAY = 'https:///gateway.do?' if DEBUG else 'https:///gateway.do?'10. 配置⽂件中配置⽀付宝替换接⼝:settings.py|开发⼈员# 后台基URLBASE_URL = 'http://139.196.184.91:8000' # 注意: 这⾥的8000上线以后指定的nginx的8000端⼝, 由nginx的8000端⼝发送到nginx配置内部的uwsgi的端⼝中# 前台基URLLUFFY_URL = 'http://139.196.184.91' # 注意: 这⾥没有写端⼝默认就是80端⼝.# ⽀付宝同步异步回调接⼝配置# 后台: ⽀付宝异步回调的接⼝NOTIFY_URL = BASE_URL + "/order/success/"# 前台: ⽀付宝同步回调接⼝,没有 / 结尾RETURN_URL = LUFFY_URL + "/pay/success"五、后台-⽀付接⼝1. 订单模块表<1>. 流程'''1. 新建订单app, 注册, ⼦路由urls, 总路由分发,2. 表分析订单表:订单标题, 总价格, 订单id(⾃⼰的), 流⽔号(⽀付宝), 订单状态, ⽀付⽅式, ⽀付时间, 订单⽤户(注意: 导⼊⽤户表路径尽量⼩), 创建时间, 更新时间订单⼀对多外键, 课程⼀对多外键(级联删除改为Set_NULL, null=True), 原价格, 实价str的健壮性校验订单和订单详情表关系分析: ⼀对多订单详情是多的⼀⽅⼀个订单可以有多个订单详情, ⼀个订单详情不可以同时属于多个订单.订单表和课程表关系分析: 多对多⼀个订单可以包含多个课程, ⼀个课程可以属于多个订单重点: 但是我们这⾥不着不过对订单表与课程表建⽴多对多的关系,⽽是通过订单详情表与课程表建⽴关系.订单详情表和课程表关系分析: ⼀对多订单详情是多的⼀⽅订单详情多的⼀⽅⼀个订单详情不可以属于多个课程, ⽽⼀个课程可以属于多个订单详情订单表和⽤户表关系分析: ⼀对多订单是多的⼀⽅⼀个⽤户可以下多个订单, ⼀个订单不能属于多个⽤户on_delete -> DO_NOTHINGdb_constraint=False提⽰: 不继承BaseModel表. is_show, orders没有必要存在3. 数据迁移'''<2>. order/models.py"""class Order(models.Model):# 主键、总⾦额、订单名、订单号、订单状态、创建时间、⽀付时间、流⽔号、⽀付⽅式、⽀付⼈(外键) - 优惠劵(外键,可为空)passclass OrderDetail(models.Model):# 订单号(外键)、商品(外键)、实价、成交价 - 商品数量pass"""from django.db import modelsfrom user.models import Userfrom course.models import Courseimport utilsclass Order(models.Model):"""订单模型"""status_choices = ((0, '未⽀付'),(1, '已⽀付'),(2, '已取消'),(3, '超时取消'),)pay_choices = ((1, '⽀付宝'),(2, '微信⽀付'),)subject = models.CharField(max_length=150, verbose_name="订单标题")total_amount = models.DecimalField(max_digits=10, decimal_places=2, verbose_name="订单总价", default=0)out_trade_no = models.CharField(max_length=64, verbose_name="订单号", unique=True)trade_no = models.CharField(max_length=64, null=True, verbose_name="流⽔号")order_status = models.SmallIntegerField(choices=status_choices, default=0, verbose_name="订单状态")pay_type = models.SmallIntegerField(choices=pay_choices, default=1, verbose_name="⽀付⽅式")pay_time = models.DateTimeField(null=True, verbose_name="⽀付时间")created_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')updated_time = models.DateTimeField(auto_now=True, verbose_name='更新时间')# 订单表和⽤户表关系分析: ⼀对多订单是多的⼀⽅⼀个⽤户可以下多个订单, ⼀个订单不能属于多个⽤户user = models.ForeignKey(User, related_name='order_user', on_delete=models.DO_NOTHING, db_constraint=False,verbose_name="下单⽤户")class Meta:db_table = "luffy_order"verbose_name = "订单记录"verbose_name_plural = "订单记录"def __str__(self):return "%s - ¥%s" % (self.subject, self.total_amount)@propertydef courses(self):data_list = []for item in self.order_courses.all():data_list.append({"id": item.id,"course_name": ,"real_price": item.real_price,})return data_listclass OrderDetail(models.Model):"""订单详情"""price = models.DecimalField(max_digits=6, decimal_places=2, verbose_name="课程原价")real_price = models.DecimalField(max_digits=6, decimal_places=2, verbose_name="课程实价")# 订单和订单详情表关系分析: ⼀对多订单详情是多的⼀⽅⼀个订单可以有多个订单详情, ⼀个订单详情不可以同时属于多个订单.order = models.ForeignKey(Order, related_name='order_courses', on_delete=models.CASCADE, db_constraint=False,verbose_name="订单")# 订单详情表和课程表关系分析: ⼀对多订单详情是多的⼀⽅订单详情多的⼀⽅⼀个订单详情不可以属于多个课程, ⽽⼀个课程可以属于多个订单详情 '''订单表和课程表关系分析: 多对多⼀个订单可以包含多个课程, ⼀个课程可以属于多个订单重点: 但是我们这⾥不着不过对订单表与课程表建⽴多对多的关系,⽽是通过订单详情表与课程表建⽴关系.'''course = models.ForeignKey(Course, related_name='course_orders', on_delete=models.SET_NULL, null=True,db_constraint=False,verbose_name="课程")class Meta:db_table = "luffy_order_detail"verbose_name = "订单详情"verbose_name_plural = "订单详情"def __str__(self):"""str的健壮性校验"""try:return "%s的订单:%s" % (, self.order.out_trade_no)except Exception as e:utils.log.error(str(e))return super().__str__()2. 订单模块接⼝之⽀付接⼝<1>. 流程'''1. ⽀付接⼝: ⽣成订单, ⽣成⽀付连接, 返回⽀付连接1) 新建路由pay, payView2) 新建视图payVieworder表和orderdetail表插⼊数据, 重写create⽅法.⽣成订单号 uuid登录后才能⽀付 jwt认证当前登录⽤户就是下单⽤户, 存到order表中订单价格校验. 如: 下了三个课程, 总价格100, 前端提交的价格是99# 实现继承 C, G新建序列化类 OrderModelSeriailzer注意: 这是⼀个反序列化的表# 传输的数据格式{course: [1, 2, 3], total_amount: 100, subject: 商品名, pay_type: 1}# 控制字段fields=['total_amount', 'subject', 'pay_type', 'course_list']# 可以再局部钩⼦中把course=[1, 2, 3]⽣成course=[obj1, obj2, obj3] 或者使⽤ PrimayKeyRElatedFieldcourse=serialisers.CharField()# 校验1. 校验订单总价格: 获取总价格, 获取课程对象列表从总价格列表中获取每个价格叠加与总价格对⽐ (注意: 需要返回总价格)2. ⽣成订单号: str(uuid).replace('-', '')3. 获取⽀付⽤户: 视图中重写create⽅法借助self.context传将request对象传给序列化类4. ⽣成⽀付连接: 导⼊alipay, alipay_gateway. 拷贝, 将post, get2个回调的地址存放到配置⽂件中(配置到django的配置⽂件中), 拼接地址返回即可!5. ⼊库(订单, 订单详情): 将user对象存⼊attrs中, 把订单号存⼊attrs中, 将pay_url存⼊self.context中6. create⽅法. 先pop出课程列表对象, 存order表. for循环存⼊课程详情视图中: Response返回给前端的, 前端只需要⼀个连接, 那么序列化校验的第五步, 在self.context中将它存⼊, 将它返回给前端3) 配置jwt认证对PayView类进⾏限制. 使⽤内置限制(认证 + 权限)内置认证类: JSONWebTokenAUthentication内置权限类: isAuthenticated4) 序列化中让所有的fields中的字段必填. 有默认值的字段, 就不是必填的. required=True5) 出现错误: ⽀付宝⽀付的时候pay_total_amount是⼀个decimal类型, 需要转换成float类型. (提⽰: decimal累加可以)提⽰: ⽀付⽅式⽬前只写了⽀付宝的⽀付⽅式因此pay_type=1, 3个课程⼀起买⼀共1382. ⽀付宝异步回调的post接⼝: 验签, 修改订单状态3. 当⽀付宝get回调前端, vue组件⼀创建, ⽴马向后端你发⼀个get请求.(⽐较绕)'''<2>. order/views.pyfrom rest_framework.viewsets import GenericViewSetfrom rest_framework.mixins import CreateModelMixinfrom rest_framework import statusfrom rest_framework_jwt.authentication import JSONWebTokenAuthenticationfrom rest_framework.permissions import IsAuthenticatedimport utilsfrom . import modelsfrom . import serializerclass PayView(CreateModelMixin, GenericViewSet):# 对PayView类进⾏限制. 使⽤内置限制(认证 + 权限)authentication_classes = [JSONWebTokenAuthentication]permission_classes = [IsAuthenticated]queryset = models.Order.objects.all()serializer_class = serializer.OrderModelSeriailzerdef create(self, request, *args, **kwargs):# 视图中重写create⽅法借助self.context传将request对象传给序列化类serializer = self.get_serializer(data=request.data, context={'request': request})serializer.is_valid(raise_exception=True)self.perform_create(serializer)headers = self.get_success_headers(serializer.data)# 视图中: Response返回给前端的, 前端只需要⼀个连接, 那么序列化校验的第五步, 在self.context中将它存⼊, 将它返回给前端return utils.APIResponse(serializer.context['pay_link'], status=status.HTTP_201_CREATED, headers=headers)<3>. order/serializer.pyimport uuidfrom rest_framework import serializersfrom rest_framework.exceptions import ValidationErrorfrom django.conf import settingsfrom . import modelsfrom libs.alipay_sdk import alipay, alipay_gatewayclass OrderModelSeriailzer(serializers.ModelSerializer):# 可以再局部钩⼦中把course_list=[1, 2, 3]⽣成course_list=[obj1, obj2, obj3] 或者使⽤ PrimayKeyRElatedFieldcourse_list = serializers.PrimaryKeyRelatedField(write_only=True, many=True, queryset=models.Course.objects.all())class Meta:model = models.Orderfields = ['subject', 'total_amount', 'pay_type', 'course_list']extra_kwargs = {# 序列化中让所有的fields中的字段必填. 有默认值的字段, 就不是必填的. required=True'total_amount': {'required': True},'pay_type': {'required': True},}@staticmethoddef _verify_amount(attrs):total_amount = attrs.get('total_amount')course_list = attrs.get('course_list')course_amount = 0for course in course_list:course_amount += course.priceif course_amount == total_amount:return total_amountraise ValidationError("订单总价错误!")@staticmethoddef _order_number():return str(uuid.uuid1()).replace('-', '')def _pay_user(self):return self.context['request'].userdef _pay_link(self, out_trade_no, total_amount, subject):# print('total_amount:', total_amount, type(total_amount)) # total_amount: 138.00 <class 'decimal.Decimal'>order_string = alipay.api_alipay_trade_page_pay(out_trade_no=out_trade_no, # 订单号, 必须唯⼀# ⽀付宝⽀付的时候pay_total_amount是⼀个decimal类型, 需要转换成float类型. (提⽰: decimal累加可以)total_amount=float(total_amount), # 总⾦额subject=subject, # 订单标题return_url=settings.RETURN_URL, # 同步回调(⽀付成功)notify_url=settings.NOTIFY_URL # 异步回调(订单状态) 可选, 不填则使⽤默认notify url)return alipay_gateway + order_stringdef _before_create(self, attrs, out_trade_no, user, pay_link):attrs['out_trade_no'] = out_trade_noattrs['user'] = userself.context['pay_link'] = pay_linkdef validate(self, attrs):"""1. 校验订单总价格: 获取总价格, 获取课程对象列表从总价格列表中获取每个价格叠加与总价格对⽐ (注意: 需要返回总价格)2. ⽣成订单号: str(uuid).replace('-', '')3. 获取⽀付⽤户: 视图中重写create⽅法借助self.context传将request对象传给序列化类4. ⽣成⽀付连接: 导⼊alipay, alipay_gateway. 拷贝, 将post, get2个回调的地址存放到配置⽂件中(配置到django的配置⽂件中), 拼接地址返回即可!5. ⼊库(订单, 订单详情): 将user对象存⼊attrs中, 将pay_link存⼊self.context中"""# 1. 校验订单总价格total_amount = self._verify_amount(attrs)# 2. ⽣成订单号order_number = self._order_number()# 3. 获取⽀付⽤户user = self._pay_user()# 4. ⽣成⽀付连接pay_link = self._pay_link(out_trade_no=order_number, total_amount=total_amount, subject=attrs.get('subject'))# 5. ⼊库(订单, 订单详情)self._before_create(attrs=attrs, out_trade_no=order_number, user=user, pay_link=pay_link)return attrsdef create(self, validated_data):course_list = validated_data.pop('course_list')order = models.Order.objects.create(**validated_data)for course in course_list:models.OrderDetail.objects.create(course=course, price=course.price, real_price=course.price, order=order)return order<4>. settings/dev.py# 后台基URLBASE_URL = 'http://139.196.184.91'# 前台基URLLUFFY_URL = 'http://139.196.184.91'# ⽀付宝同步异步回调接⼝配置# 后台异步回调接⼝NOTIFY_URL = BASE_URL + "/order/success/"# 前台同步回调接⼝,没有 / 结尾RETURN_URL = LUFFY_URL + "/pay/success"<5>. luffyapi/urls.pypath('order/',include('order.urls')),<6>. order/urls.py⼦路由from django.urls import path, re_path, includefrom . import viewsfrom rest_framework.routers import SimpleRouterrouter = SimpleRouter()router.register('pay', views.PayView, 'pay')urlpatterns = [path('', include(router.urls)),]六、前台-⽀付⽣成页⾯1. 前端跳转到⽀付宝⽀付<1>. 流程'''提⽰: ⼀共三个地⽅都有⽴即购买操作1. FreeCourse.vue1) 定义buy_now()点击触发事件的⽅法从this.$cookies中获取token判断如果没有token那么触发this.$message发送ajax的post请求, this.$settings.base_url + /order/pay/, headers需要携带认证 Authorization, data需要携带对着数据. 使⽤另⼀种⽤法{}获取到pay_link, 前端发送get请求window.open(pay_link, '_self')2) 付款成功以后需要跳转到/order/success页⾯, 前端需要success组件. 后端需要success接⼝'''<2>. FreeCoourse.vue# template<span class="buy-now" @click="buy_now(course)">⽴即购买</span># scriptmethods: {buy_now(course) {// 获取token, 校验⽤户是否登录let token = this.$cookies.get('token');if (!token) {this.$message({message: "请先登录!",type: 'warning',});return false;}// 发送axiosthis.$axios({method: 'post',url: `${this.$settings.base_url}/order/pay/`,data: {"subject": ,// "total_amount": 11,"total_amount": course.price,"pay_type": 1,"course_list": [course.id,},headers: {Authorization: `jwt ${this.$cookies.get('token')}`},}).then(response => {console.log(response.data);if (response.data.code) {open(response.data.data, '_self');} else {this.$message({message: '订单处理失败!',type: 'warning',})}}).catch(error => {this.$message({message: "未知错误!",type: 'warning',})})},...}2. ⽀付成功前端页⾯<1>. 流程'''1. 新建PaySuccess.vue组件2. 配置路由 path: '/pay/success'注意: 回调以后会在你的url地址中, 携带者很多东西3. 拷贝PaySuccess页⾯提⽰: 页⾯只有⽀付宝回调回来才有数据, 直接查看是没有的4. create⾥⾯有⼀种特殊⽤法5. 同步回调参数trade_no ⽀付宝的流⽔号auth_app_id 商家流⽔号app_id 我们的id号页⾯需要的参数: 订单号, 交易号, 付款时间'''<2>. routere/index.jsimport PaySuccess from '../views/PaySuccess.vue'const routes = [...{path: '/pay/success',name: 'PaySuccess',component: PaySuccess},];<3>. ⽀付宝返回参数charset=utf-8&out_trade_no=7f7c7d12d57d45b693e1b49a6b01e1dd& # ⾃⼰的订单号method=alipay.trade.page.pay.return&total_amount=39.00&sign=FUmceqiNMWvxcD%2BUPCHiOTaEwlJ%2FXIXL5UwZWOSI1TwRjPIZVzjRLB4j2G5CQpn472JO8X%2BwMx04dHqjLxqLcY3TRu0XurQ%2FwKTNpyfDrtNuNv0rfGPuVHw52y3blbS7%2FKFVsWryw4%2BBuF2fCrJ4qWH8Zg14Rct7qoMbu73N trade_no=2020030722001464020500585462& # ⽀付宝的流⽔号auth_app_id=2016093000631831&version=1.0&app_id=2016093000631831&sign_type=RSA2&seller_id=2088102177958114×tamp=2020-03-07%2014%3A47%3A48 # 付款时间`// 同步回调没与订单状态<4>. views/PaySuccess.vue<template><div class="pay-success"><!--如果是单独的页⾯,就没必要展⽰导航栏(带有登录的⽤户)--><Header/><div class="main"><div class="title"><div class="success-tips"><p class="tips">您已成功购买 1 门课程!</p></div></div><div class="order-info"><p class="info"><b>订单号:</b><span>{{ result.out_trade_no }}</span></p><p class="info"><b>交易号:</b><span>{{ result.trade_no }}</span></p><p class="info"><b>付款时间:</b><span><span>{{ result.timestamp }}</span></span></p></div><div class="study"><span>⽴即学习</span></div></div></div></template><script>import Header from "@/components/Header"export default {name: "Success",data() {return {result: {},};},// console.log(location.search);// 解析⽀付宝回调的url参数let params = location.search.substring(1); // 去除? => a=1&b=2 let items = params.length ? params.split('&') : []; // ['a=1', 'b=2']//逐个将每⼀项添加到args对象中for (let i = 0; i < items.length; i++) { // 第⼀次循环a=1,第⼆次b=2 let k_v = items[i].split('='); // ['a', '1']//解码操作,因为查询字符串经过编码的if (k_v.length >= 2) {// url编码反解let k = decodeURIComponent(k_v[0]);this.result[k] = decodeURIComponent(k_v[1]);// 没有url编码反解// this.result[k_v[0]] = k_v[1];}}// 解析后的结果// console.log(this.result);// 把地址栏上⾯的⽀付结果,再get请求转发给后端this.$axios({url: this.$settings.base_url + '/order/success/' + location.search, method: 'get',}).then(response => {console.log(response.data);}).catch(() => {console.log('⽀付结果同步失败');})},components: {Header,}}</script><style scoped>.main {padding: 60px 0;margin: 0 auto;width: 1200px;background: #fff;}.main .title {display: flex;-ms-flex-align: center;align-items: center;padding: 25px 40px;border-bottom: 1px solid #f2f2f2;}.main .title .success-tips {box-sizing: border-box;}.title img {vertical-align: middle;width: 60px;height: 60px;margin-right: 40px;}.title .success-tips {box-sizing: border-box;}.title .tips {font-size: 26px;color: #000;}.info span {color: #ec6730;}.order-info {padding: 25px 48px;padding-bottom: 15px;border-bottom: 1px solid #f2f2f2;}.order-info p {display: -ms-flexbox;display: flex;margin-bottom: 10px;font-size: 16px;}.order-info p b {font-weight: 400;color: #9d9d9d;white-space: nowrap;}.study {padding: 25px 40px;}.study span {display: block;width: 140px;height: 42px;text-align: center;line-height: 42px;cursor: pointer;background: #ffc210;border-radius: 6px;font-size: 16px;color: #fff;}</style>七、后台-⽀付成功的备选接⼝1. 流程优化: 后端序列化中判断⽤户⽀付⾦额是否是0, 是0那么就直接修改订单状态, 也不⽤发送pay_link了# 前端: created分析1. localtion.search就可以获取⽀付好?号后⾯的参数获取到(包括问号), 使⽤.substring(1), 取出左边的?号2. 使⽤三元表达式, 对params进⾏split. 以及后⾯将这种参数进⾏处理3. decodeURICompontent,4. 把地址栏上⾯的⽀付结果, 再get请求发给后端this.$settings.base_url + '/order/success/' + localtion.search# 后端1. 路由: success/ SuccessView2. 视图: 继承APIView 因为不和序列化类有关系, 和数据库有点关系# get:获取前端传递过来的 out_trade_no, 去数据库中查取, 判断订单 order_status 的订单状态是否成功.最后返回响应中通过code=0或者code=1返回给前端即可# post: ⽀付宝回调回调地址: https:///fzlee/alipay/blob/master/README.zh-hans.md#alipay.fund.trans.toaccount.transfer回调参数: https:///open/270/105902/注意: 必须data内容返回 successrequest.data可能有2种情况. 如果是json格式是字典, 如果是QuseryDict需要注意失败了之后需要记录⽇志成功了之后需要记录⽇志, 并且修改订单状态, 使⽤ out_trade_no 作为过来标志, order_status 修改为1, 交易⽀付时间pay_time=gmt_payment'''2. 同步理论参数charset=utf-8&out_trade_no=7f7c7d12d57d45b693e1b49a6b01e1dd&method=alipay.trade.page.pay.return&total_amount=39.00&sign=FUmceqiNMWvxcD%2BUPCHiOTaEwlJ%2FXIXL5UwZWOSI1TwRjPIZVzjRLB4j2G5CQpn472JO8X%2BwMx04dHqjLxqLcY3TRu0XurQ%2FwKTNpyfDrtNuNv0rfGPuVHw52y3blbS7%2FKFVsWryw4%2BBuF2fCrJ4qWH8Zg14Rct7qoMbu73N trade_no=2020030722001464020500585462&auth_app_id=2016093000631831&version=1.0&app_id=2016093000631831&sign_type=RSA2&seller_id=2088102177958114×tamp=2020-03-07%2014%3A47%3A48`// 同步回调没与订单状态3. order/urls.pypath('success/',views.successView.as_view()),4. order/views.pyfrom rest_framework.views import APIViewfrom libs.alipay_sdk import alipayclass SuccessView(APIView):def get(self, request, *args, **kwargs):"""获取前端传递过来的 out_trade_no, 去数据库中查取, 判断订单 order_status 的订单状态是否成功.最后返回响应中通过code=0或者code=1返回给前端即可"""out_trade_no = request.query_params.get('out_trade_no')order = models.Order.objects.filter(out_trade_no=out_trade_no).first()# order.order_status值为1表⽰订单成功if order.order_status == 1:return utils.APIResponse()return utils.APIResponse(code=0, msg='失败')def post(self, request, *args, **kwargs):"""回调地址: https:///fzlee/alipay/blob/master/README.zh-hans.md#alipay.fund.trans.toaccount.transfer回调参数: https:///open/270/105902/注意: 必须data内容返回 successrequest.data可能有2种情况. 如果是json格式是字典, 如果是QuseryDict需要注意失败了之后需要记录⽇志成功了之后需要记录⽇志, 并且修改订单状态, 使⽤ out_trade_no 作为过来标志, order_status修改为1, 交易⽀付时间pay_time=gmt_payment"""# request.data类型判断data = request.data.dict()utils.log(f'data: {data}')signature = data.pop("sign")out_trade_no = data.get('out_trade_no')gmt_payment = data.get('gmt_payment')# 校验success = alipay.verify(data, signature)if success and data["trade_status"] in ("TRADE_SUCCESS", "TRADE_FINISHED"):# 修改订单状态models.Order.objects.filter(out_trade_no=out_trade_no).update(order_status=1, pay_time=gmt_payment)(f'{out_trade_no}订单⽀付成功!')# 注意: 服务器异步通知页⾯特性'''当商户收到服务器异步通知并打印出 success 时,服务器异步通知参数 notify_id 才会失效。
php实现微信和⽀付宝⽀付的⽰例代码php实现微信⽀付在php下实现微信⽀付,这⾥我使⽤了EasyWeChat这⾥我是在Yii框架实现的,安装EasyWeChat插件composer require jianyan74/yii2-easy-wechat⼀:配置EasyWeChat1:在config/main.php 的 component中添加EasyWeChat的SDK'components' => [// ...'wechat' => ['class' => 'jianyan\easywechat\Wechat','userOptions' => [], // ⽤户⾝份类参数'sessionParam' => 'wechatUser', // 微信⽤户信息将存储在会话在这个密钥'returnUrlParam' => '_wechatReturnUrl', // returnUrl 存储在会话中'rebinds' => [ // ⾃定义服务模块// 'cache' => 'common\components\Cache',]],// ...]2:在config/params.php中设置基础配置信息和微信⽀付信息// 微信配置具体可参考EasyWechat'wechatConfig' => [],// 微信⽀付配置具体可参考EasyWechat'wechatPaymentConfig' => [],// 微信⼩程序配置具体可参考EasyWechat'wechatMiniProgramConfig' => [],// 微信开放平台第三⽅平台配置具体可参考EasyWechat'wechatOpenPlatformConfig' => [],// 微信企业微信配置具体可参考EasyWechat'wechatWorkConfig' => [],// 微信企业微信开放平台具体可参考EasyWechat'wechatOpenWorkConfig' => [],// 微信⼩微商户具体可参考EasyWechat'wechatMicroMerchantConfig' => [],⼆:实现微信⽀付1:微信⽀付api$data = ['body' => '',//⽀付描述'out_trade_no' => '',//订单号'total_fee' => '',//⽀付⾦额'notify_url' => '', // ⽀付结果通知⽹址,如果不设置则会使⽤配置⾥的默认地址'trade_type' => 'JSAPI',//⽀付⽅式'openid' => '',//⽤户openid];// ⽣成⽀付配置$payment = Yii::$app->wechat->payment;$result = $payment->order->unify($data);if ($result['return_code'] == 'SUCCESS') {$prepayId = $result['prepay_id'];$config = $payment->jssdk->sdkConfig($prepayId);} else {throw new yii\base\ErrorException('微信⽀付异常, 请稍后再试');}return $this->render('wxpay', ['jssdk' => $payment->jssdk, // $app通过上⾯的获取实例来获取'config' => $config]);2:在wxpay.php⽂件中发起⽀付<script src="/open/js/jweixin-1.4.0.js" type="text/javascript" charset="utf-8"></script><script type="text/javascript" charset="utf-8">//数组内为jssdk授权可⽤的⽅法,按需添加,详细查看微信jssdk的⽅法wx.config(<?php echo $jssdk->buildConfig(array('chooseWXPay'), true) ?>);function onBridgeReady(){// 发起⽀付wx.chooseWXPay({timestamp: <?= $config['timestamp'] ?>,nonceStr: '<?= $config['nonceStr'] ?>',package: '<?= $config['package'] ?>',signType: '<?= $config['signType'] ?>',paySign: '<?= $config['paySign'] ?>', // ⽀付签名success: function (res) {// ⽀付成功后的回调函数},cancel: function(r) {//⽀付取消后的回调函数},});}if (typeof WeixinJSBridge == "undefined"){if( document.addEventListener ){document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);}else if (document.attachEvent){document.attachEvent('WeixinJSBridgeReady', onBridgeReady);document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);}}else{onBridgeReady();}</script>在异步回调地址中获取微信⽀付回调只需要使⽤如下⽅法即可:$payment = Yii::$app->wechat->payment;$response = $payment->handlePaidNotify(function($message, $fail) {//⽀付结果逻辑,只有在函数⾥ return true; 才代表处理完成});$response->send();根据如上步骤就可以实现微信⽀付php实现⽀付宝⽀付⼀:在php中安装⽀付宝插件composer require alipaysdk/easysdk⼆:php实现⽀付宝⽀付1:配置⽀付宝/*** ⽀付宝配置*/public static function getOptions(){$options = new Config();$options->protocol = 'https';$options->gatewayHost = '';$options->signType = 'RSA2';$options->appId = '<-- 请填写您的AppId,例如:2019022663440152 -->';// 为避免私钥随源码泄露,推荐从⽂件中读取私钥字符串⽽不是写⼊源码中$options->merchantPrivateKey = '<-- 请填写您的应⽤私钥,例如:MIIEvQIBADANB ... ... -->';$options->alipayCertPath = '<-- 请填写您的⽀付宝公钥证书⽂件路径,例如:/foo/alipayCertPublicKey\_RSA2.crt -->';$options->alipayRootCertPath = '<-- 请填写您的⽀付宝根证书⽂件路径,例如:/foo/alipayRootCert.crt" -->';$options->merchantCertPath = '<-- 请填写您的应⽤公钥证书⽂件路径,例如:/foo/appCertPublicKey\_2019051064521003.crt -->'; //注:如果采⽤⾮证书模式,则⽆需赋值上⾯的三个证书路径,改为赋值如下的⽀付宝公钥字符串即可// $options->alipayPublicKey = '<-- 请填写您的⽀付宝公钥,例如:MIIBIjANBg... -->';//可设置异步通知接收服务地址(可选)$options->notifyUrl = "<-- 请填写您的⽀付类接⼝异步通知接收服务地址,例如:https:///callback -->";//可设置AES密钥,调⽤AES加解密相关接⼝时需要(可选)//$options->encryptKey = "<-- 请填写您的AES密钥,例如:aa4BtZ4tspm2wnXLb1ThQA== -->";return $options;}2:实现⽀付宝⽀付//加载⽀付宝配置Factory::setOptions(self::getOptions());try {//发起API调⽤$result = Factory::payment()->wap()->pay('订单标题', '商户订单号', '订单总⾦额', '⽤户付款中途退出返回商户⽹站的地址', '⽀付回调地址');$responseChecker = new ResponseChecker();//处理响应或异常if ($responseChecker->success($result)) {//调⽤成功return $result->body;} else {//调⽤失败$errorMsg = $result->msg . $result->subMsg;throw new yii\\base\\ErrorException($errorMsg);}} catch (\\Exception $e) {throw new yii\\base\\ErrorException($e->getMessage());}根据如上就可以实现⽀付宝⽀付到此这篇关于php实现微信和⽀付宝⽀付的⽰例代码的⽂章就介绍到这了,更多相关php实现微信和⽀付宝⽀付内容请搜索以前的⽂章或继续浏览下⾯的相关⽂章希望⼤家以后多多⽀持!。
微信支付七种支付通道今天带来的是微信支付7种常见的支付通道,市面上最常见的七种微信支付通道如下所示:JSAPI支付、APP支付、付款码支付、Native支付、微信收款商业版、小程序、H5支付。
在微信支付以外,还有支付宝支付、银联支付等一些其他支付渠道,但是都没有微信支付这7种支付渠道使用的范围广。
接下来根据开发技术的难度、最适合商家的两个方面来划分这七种支付渠道的优先级。
一、划分支付通道优先级1、按技术层次划分:Native支付-> APP支付-> 小程序支付-> H5支付-> JSAPI支付-> 付款码支付->微信收款商业版(1) Native支付:优先级最高。
Native支付是指在移动应用中使用微信SDK进行支付,用户体验较好,支付成功率高。
但需要对接微信SDK,开发成本较高。
(2) APP支付:优先级较高。
APP支付是指在手机上通过微信客户端进行支付,相比H5支付更加稳定可靠。
但需要用户下载并安装微信客户端。
(3) 小程序支付:也是优先级较高。
小程序支付是指在微信小程序内部进行支付,可以提升用户体验及转化率。
但需要开发微信小程序,并通过微信审核。
(4) H5支付:优先级次之。
H5支付是指在手机浏览器中打开链接进行支付,用户体验较差,但不需要用户下载客户端,支持跨平台使用。
(5) JSAPI支付:优先级次于H5支付。
JSAPI支付是指在网页上嵌入微信支付功能,在微信内部完成支付。
但需要用户在微信中打开网页,不能跨平台使用。
(6) 付款码支付:优先级较低。
付款码支付是指商户收款时由用户出示自己的微信支付二维码进行扫码支付。
但需要用户主动出示二维码,适用范围有限。
(7) 微信收款商业版:优先级最低。
微信收款商业版是指商户通过微信官方提供的工具进行收款,可支持多种支付方式。
但需要服务费用,并且功能相比其他支付方式较为受限制。
2、按照最适合商家来划分:JSAPI支付H5支付-> APP支付和Native支付-> 微信收款商业版和付款码支付-> 小程序支付(1)微信支付JSAPI支付和H5支付:适用于网页和移动端应用内支付,方便用户快速完成支付,且接口简单易用,两者之间无明显差别,开发成本可观,是商家朋友的不二之选。
支付宝和微信支付流程和技术说明
1.1微信支付
1.1.1JSAPI网页支付
JSAPI网页支付即前文说的公众号支付,可在微信公众号、朋友圈、聊天会话中点击页面链接,或者用微信“扫一扫”扫描页面地址二维码在微
信中打开商户HTML5页面,在页面内下单完成支付。
1.1.2Native原生支付
Native原生支付即前文说的扫码支付,商户根据微信支付协议格式生成的二维码,用户通过微信“扫一扫”扫描二维码后即进入付款确认界面,输入密码即完成支付。
1.1.3接入方式
微信支付系统是指完成微信支付流程中涉及的
API接口、后台业务处理系统、账务系统、回调通知等系统的总称。
微信支付分为公众号支付,App支付,扫码支付(包括PC 网站),刷卡支付(设备扫描用户手
机)
本项目主要适用扫码支付。
接通扫码支付流程:
注册公众帐号申请微信
认证
申请微信
支付
商户平台
进行验证
在线签署
协议
启动设计
和开发
用户扫描商户展示在各种场景的二维码进行支付。
步骤1:商户根据微信支付的规则,为不同商品生成不同的二维码,展示在各种场景,用于用户扫描购买。
步骤2:用户使用微信“扫一扫”扫描二维码后,获取商品支付信息,引导用户完成支付。
步骤(3):用户确认支付,输入支付密码。
步骤(4):支付完成后会提示用户支付成功,商户后台得到支付成功的通知,然后进行发货处理。
开发步骤:。
前端h5调起⽀付宝扫码付款,附上启动⽀付宝常⽤命令在开发⼀些⽀付宝相关功能的⽹页时,有些情况需要直接调起⽀付宝APP的某个功能,那么在前端直接⽤⼀段简单的命令来调起是最⽅便的。
⽤法也简单,直接⽤⼀个a标签来跳转就可以。
举个例⼦,⽐如我项⽬中使⽤的调起⽀付宝扫码付款——<a href='alipays://platformapi/startapp?appId=20000067&url=⽀付宝收款码⽀付地址'> 跳转⽀付宝⽀付</a>然后,有的时候这样也没办法成功调起,那么再加个前缀转化⼀下。
https:///p/s/i?scheme=PS:注意要把等号后⾯这串地址urlencode编码转化。
下⾯是⼀些常⽤的唤醒⽀付宝某项功能的命令mes = “alipayqr://platformapi/startapp?saId=10000003”;⼿机充值mes = “alipayqr://platformapi/startapp?saId=10000007”;扫⼀扫mes = “alipayqr://platformapi/startapp?saId=10000009”;爱⼼捐赠mes = “alipayqr://platformapi/startapp?saId=100000011”;彩票⾸页mes = “alipayqr://platformapi/startapp?saId=100000033”;话费卡转让mes = “alipayqr://platformapi/startapp?saId=10000110”;关于mes = “alipayqr://platformapi/startapp?saId=10000112”;服务授权mes = “alipayqr://platformapi/startapp?saId=20000116”;转账mes = “alipayqr://platformapi/startapp?saId=20000056”;付款码mes = alipays://platformapi/startapp?appId=20000003;账单mes = alipays://platformapi/startapp?appId=20000076;账单mes = “alipays://platformapi/startapp?appId=20000006”;切换账户mes = “alipays://platformapi/startapp?appId=20000008”;⽀付宝登出mes = “alipays://platformapi/startapp?appId=20000009”;⼿机号注册mes = “alipays://platformapi/startapp?appId=20000010”;绑定⼿机mes = “alipays://platformapi/startapp?appId=20000011”;客服帮助mes = “alipays://platformapi/startapp?appId=20000013”;修改⽀付密码mes = “alipays://platformapi/startapp?appId=20000014”;我的银⾏卡mes = “alipays://platformapi/startapp?appId=20000015”;找回登录密码mes = “alipays://platformapi/startapp?appId=20000017”;修改登录密码mes = “alipays://platformapi/startapp?appId=20000019”;余额mes = “alipays://platformapi/startapp?appId=20000020”;卡包mes = “alipays://platformapi/startapp?appId=20000024”;⽀付宝设置mes = “alipays://platformapi/startapp?appId=20000027”;账号切换mes = “alipays://platformapi/startapp?appId=20000031”;设置个⼈头像mes = “alipays://platformapi/startapp?appId=20000032”;余额宝mes = “alipays://platformapi/startapp?appId=20000033”;提现mes = “alipays://platformapi/startapp?appId=20000038”;⾝份验证mes = “alipays://platformapi/startapp?appId=20000048”;添加⽣活好mes = “alipays://platformapi/startapp?appId=20000049”;意见反馈mes = “alipays://platformapi/startapp?appId=20000050”;打开地图mes = “alipays://platformapi/startapp?appId=20000057”;账号管理mes = “alipays://platformapi/startapp?appId=20000068”;快速挂失mes = “alipays://platformapi/startapp?appId=20000068”;安全中⼼mes = “alipays://platformapi/startapp?appId=20000071”;城市⼀卡通mes = “alipays://platformapi/startapp?appId=20000078”;上银汇款mes = “alipays://platformapi/startapp?appId=20000081”;更多mes = “alipays://platformapi/startapp?appId=20000122”;⾸页活动mes = “alipays://platformapi/startapp?appId=20000123”;收钱mes = “alipayqr://platformapi/startapp?appId=68687017”;年度账单mes = “alipayqr://platformapi/startapp?appId=20000101”;⽣活号mes = “alipayqr://platformapi/startapp?appId=20000102”;打开nfcmes = “alipayqr://platformapi/startapp?appId=20000107”;出境mes = “alipayqr://platformapi/startapp?appId=20000108”;挂号就诊mes = “alipayqr://platformapi/startapp?appId=20000110”;我的保障mes = “alipayqr://platformapi/startapp?appId=20000115”;设备管理mes = “alipayqr://platformapi/startapp?appId=20000119”;阿⾥游戏mes = “alipayqr://platformapi/startapp?appId=20000118”;芝⿇信⽤mes = “alipayqr://platformapi/startapp?appId=20000120”;饿了么mes = “alipayqr://platformapi/startapp?appId=20000123”;收钱mes = “alipayqr://platformapi/startapp?appId=20000125”;⾸页mes = “alipayqr://platformapi/startapp?appId=20000126”;免费wifimes = “alipayqr://platformapi/startapp?appId=20000130”;滴滴mes = “alipayqr://platformapi/startapp?appId=20000132”;亲情号mes = “alipayqr://platformapi/startapp?appId=20000134”;股票⾃选mes = “alipayqr://platformapi/startapp?appId=20000135”;⽕车票mes = “alipayqr://platformapi/startapp?appId=20000136”;游戏充值mes = “alipayqr://platformapi/startapp?appId=20000139”;酒店搜索mes = “alipayqr://platformapi/startapp?appId=20000141”;修改昵称mes = “alipayqr://platformapi/startapp?appId=20000142”;娱乐宝mes = “alipayqr://platformapi/startapp?appId=20000143”;⽕车票汽车票预定mes = “alipayqr://platformapi/startapp?appId=20000146”;我的淘宝mes = “alipayqr://platformapi/startapp?appId=20000150”;汇率换算mes = “alipayqr://platformapi/startapp?appId=20000153”;游戏中⼼mes = “alipayqr://platformapi/startapp?appId=20000155”;飞猪mes = “alipayqr://platformapi/startapp?appId=20000157”;国际机票查询mes = “alipayqr://platformapi/startapp?appId=20000160”;蚂蚁会员mes = “alipayqr://platformapi/startapp?appId=20000161”;理财⼩⼯具mes = “alipayqr://platformapi/startapp?appId=20000162”;⽺城通mes = “alipayqr://platformapi/startapp?appId=20000165”;定期理财mes = “alipayqr://platformapi/startapp?appId=20000161”;指纹⼿势解锁mes = “alipayqr://platformapi/startapp?appId=20000168”;年度账单mes = “alipayqr://platformapi/startapp?appId=20000176”;红包mes = “alipayqr://platformapi/startapp?appId=20000183”;设置⼿势密码mes = “alipayqr://platformapi/startapp?appId=20000161”;指纹⼿势解锁设定界⾯mes = “alipayqr://platformapi/startapp?appId=20000186”;通讯录mes = “alipayqr://platformapi/startapp?appId=20000161”;绑定智能⼿环mes = “alipayqr://platformapi/startapp?appId=20000197”;⾸页-热门游戏mes = “alipayqr://platformapi/startapp?appId=20000199”;花呗mes = “alipayqr://platformapi/startapp?appId=20000205”;亲情圈mes = “alipayqr://platformapi/startapp?appId=20000218”;黄⾦mes = “alipayqr://platformapi/startapp?appId=20000225”;借条mes = “alipayqr://platformapi/startapp?appId=20000227”;卡包mes = “alipayqr://platformapi/startapp?appId=20000234”;刷脸mes = “alipayqr://platformapi/startapp?appId=20000235”;服务提醒mes = “alipayqr://platformapi/startapp?appId=20000241”;车险服务mes = “alipayqr://platformapi/startapp?appId=20000243”;总资产mes = “alipayqr://platformapi/startapp?appId=20000248”;个性签名mes = “alipayqr://platformapi/startapp?appId=20000252”;朋友模块mes = “alipayqr://platformapi/startapp?appId=20000255”;账户充值mes = “alipayqr://platformapi/startapp?appId=20000266”;邮箱账单mes = “alipayqr://platformapi/startapp?appId=20000288”;聊天室mes = “alipayqr://platformapi/startapp?appId=20000290”;可能认识的⼈mes = “alipayqr://platformapi/startapp?appId=20000298”;证书管理mes = “alipayqr://platformapi/startapp?appId=20000301”;多设备管理mes = “alipayqr://platformapi/startapp?appId=20000305”;⽀付宝内付款码声波付mes = “alipayqr://platformapi/startapp?appId=20000307”;暗号还有其他的欢迎评论分享⼀下~。
扫码支付方案扫码支付已成为当代社会中非常常见的支付方式之一。
通过扫描二维码,用户可以快速便捷地完成支付操作。
在这篇文章中,我将为您介绍几种常见的扫码支付方案,并分析它们的优缺点。
同时,我也将讨论扫码支付在未来的发展趋势。
一、支付宝扫码支付支付宝扫码支付是目前国内最常见的扫码支付方案之一。
用户只需要打开支付宝应用,选择“扫一扫”功能,然后对准商家提供的二维码进行扫描即可完成支付。
支付宝扫码支付具有以下优点:首先,支付宝用户众多,几乎每个人都可以使用;其次,支付宝的支付流程简便快捷,省时省力;再次,支付宝对用户的资金安全有很好的保障机制。
然而,支付宝扫码支付也存在一些问题,例如对于没有安装支付宝应用的用户来说,使用起来就不太方便。
二、微信扫码支付微信扫码支付是另一种主流的扫码支付方案。
与支付宝扫码支付类似,用户打开微信应用后选择“扫一扫”功能,并对准商家提供的二维码进行扫描即可完成支付。
微信扫码支付的优点如下:首先,微信拥有庞大的用户基础,用户数量多;其次,微信扫码支付相对便捷,使用起来简单;再次,微信支付也有很好的资金安全机制。
然而,与支付宝扫码支付一样,微信扫码支付也存在应用未安装的用户无法使用的问题。
三、银联云闪付银联云闪付是银联推出的一种扫码支付方案。
用户只需打开银联云闪付应用,选择“扫码支付”功能后对准商家二维码扫描即可完成支付。
银联云闪付的优点在于:首先,银联云闪付与多家银行合作,用户数量庞大;其次,银联作为国内主要支付网关,对用户资金的安全性保障较高。
不过,使用银联云闪付进行扫码支付也需要安装相应的应用。
四、苹果支付苹果支付是苹果公司推出的扫码支付解决方案。
用户在iPhone或者iPad等苹果设备上添加银行卡,然后选择“扫码支付”功能完成支付。
苹果支付的优点如下:首先,苹果拥有庞大的用户群体,用户数量众多;其次,苹果支付的安全性较高,用户的资金相对较为安全。
但是,苹果支付只适用于苹果设备用户,而且对于部分老款设备可能不支持。
第1篇一、微信支付简介微信支付是腾讯公司推出的一款便捷的移动支付产品,用户可以通过微信客户端进行线上支付、线下支付等多种支付场景。
微信支付具有安全性高、便捷性强、用户量大等特点,广泛应用于电子商务、O2O、线下零售等多个领域。
二、微信支付流程1. 订单创建(1)商户在后台创建订单,订单信息包括商品名称、订单号、订单金额等。
(2)商户将订单信息发送至微信支付平台。
2. 统一下单(1)微信支付平台收到商户订单信息后,进行订单验证。
(2)验证通过后,微信支付平台生成支付参数,包括订单号、金额、签名等。
(3)微信支付平台将支付参数发送给商户。
3. 发起支付(1)商户将支付参数传递给前端页面。
(2)前端页面根据支付参数生成支付界面,用户进行支付操作。
4. 支付通知(1)用户完成支付后,微信支付平台发送支付通知给商户。
(2)商户接收支付通知,验证通知签名。
(3)验证通过后,商户处理支付结果,如修改订单状态、发送通知给用户等。
5. 退款(1)用户或商户发起退款请求。
(2)商户验证退款请求,将退款信息发送至微信支付平台。
(3)微信支付平台处理退款请求,将退款金额返还给用户。
三、PHP面试题1. 请简述微信支付的基本流程。
答:微信支付的基本流程包括订单创建、统一下单、发起支付、支付通知和退款。
2. 微信支付中,商户需要提供哪些订单信息?答:商户需要提供订单号、商品名称、订单金额等信息。
3. 微信支付中,如何生成支付参数?答:商户可以使用微信支付平台提供的API接口,根据订单信息和商户账号生成支付参数。
4. 在微信支付流程中,支付通知的作用是什么?答:支付通知是微信支付平台通知商户支付结果的一种方式,商户可以通过支付通知处理支付结果,如修改订单状态、发送通知给用户等。
5. 请简述微信支付退款流程。
答:微信支付退款流程包括用户或商户发起退款请求、商户验证退款请求、将退款信息发送至微信支付平台、微信支付平台处理退款请求、退款金额返还给用户。
微信⽀付demo这⾥开发时使⽤的是thinkphp5.11.直接唤起微信⽀付<?phpnamespace app\index\controller;use wechatpay\lib\WxPayApi;use wechatpay\lib\WxPayUnifiedOrder;use wechatpay\example\JsApiPay;use wechatpay\example\WxPayConfig;use think\facade\Env;class Pay{/*1、交易⾦额交易⾦额默认为⼈民币交易,接⼝中参数⽀付⾦额单位为【分】,参数值不能带⼩数。
对账单中的交易⾦额单位为【元】。
外币交易的⽀付⾦额精确到币种的最⼩单位,参数值不能带⼩数点。
2、交易类型JSAPI--公众号⽀付、NATIVE--原⽣扫码⽀付、APP--app⽀付,统⼀下单接⼝trade_type的传参可参考这⾥MICROPAY--刷卡⽀付,刷卡⽀付有单独的⽀付接⼝,不调⽤统⼀下单接⼝3、货币类型货币类型的取值列表:CNY:⼈民币4、时间标准北京时间,时区为东⼋区;如果商户的系统时间为⾮标准北京时间。
参数值必须根据商户系统所在时区先换算成标准北京时间,例如商户所在地为0时区的伦敦,当地时间为2014年11⽉11⽇0时0分0秒,换算成北京时间为2014年11⽉11⽇8时 5、时间戳标准北京时间,时区为东⼋区,⾃1970年1⽉1⽇ 0点0分0秒以来的秒数。
注意:部分系统取到的值为毫秒级,需要转换成秒(10位数字)。
6、商户订单号商户⽀付的订单号由商户⾃定义⽣成,仅⽀持使⽤字母、数字、中划线-、下划线_、竖线|、星号*这些英⽂半⾓字符的组合,请勿使⽤汉字或全⾓等特殊字符。
微信⽀付要求商户订单号保持唯⼀性(建议根据当前系统时间加随机序列来⽣成订单号) *//*** 微信⽀付调⽤* @author ffx* @param int $userId ⽤户id* @param float $money ⾦额* @param int $type ⽀付类型(1充值,2订单⽀付)* @param string $body 显⽰title,提⽰头信息* @param string $backUrl 回调地址(/index/pay/wxPayBack)* @param string $addition 附加参数(微信回调时原样返回)* @return bool|mixed*/public function wxPay($userId,$money,$type,$body,$backUrl,$addition = ''){$out_trade_no = self::getOrderNo();$wxchid = config('wechat.wxmchid');$wxAppId = config('wechat.paywxAppId');//微信⽀付sdk中的⽂件include_once EXTEND_PATH."wechatpay/lib/WxPay.Api.php";include_once EXTEND_PATH."wechatpay/example/WxPay.JsApiPay.php";$total_money = floor(sprintf("%0.3f",$money)*100);if(!$total_money || $total_money <= 0){return ['code'=>100001,'message'=>"⽀付⾦额⼩于0"];}if($_SERVER["REQUEST_SCHEME"] == "http"){$notify_url = 'http://'.$_SERVER['HTTP_HOST'].$backUrl;}else{$notify_url = 'https://'.$_SERVER['HTTP_HOST'].$backUrl;}//此处查询的是登录时存起来的openid$openid = Db::name('member')->where('id',$userId)->value('openid');$input = new \WxPayUnifiedOrder();$input->SetAppid($wxAppId);$input->SetMch_id($wxchid); //商户号$input->SetAttach($addition); //附加参数$nonceStr = self::createNonceStr();$input->SetNonce_str($nonceStr);//⾃定义随机字符串$input->SetBody($body);//$input->SetOut_trade_no($out_trade_no);//订单号,⾃⼰建$input->SetTotal_fee($total_money);//⾦钱$input->SetSpbill_create_ip($this->getIP());//客户端ip$input->SetTime_start(date("YmdHis"));$input->SetTime_expire(date("YmdHis", time() + 600));//过期时间$input->SetNotify_url($notify_url);//回调的接⼝地址$input->SetTrade_type("JSAPI");//⽀付类型$input->SetOpenid($openid);//此处两个是微信⽀付的sdk的类$WxPayApi = new WxPayApi();$config = new WxPayConfig();$response = $WxPayApi->unifiedOrder($config,$input);//预创建订单//写⼊⽀付记录表(业务逻辑处理)$res = $this->insertWxPayLog($userId,$out_trade_no,$money,$type,0);if(!$res){return false;}if($response['result_code']=='SUCCESS' && $response['return_code']=='SUCCESS' && $response['return_msg']=='OK') {$tools = new JsApiPay();$response['nonce_str'] = $nonceStr;$jsApiParameters = $tools->GetJsApiParameters($response);return json_decode($jsApiParameters);//$jsApiParameters⽰例//{"appId":"wx194a1075c8bca3d9","nonceStr":"4tmc7nttscu5w6u6cqbxlgvum4qs6p8y","package":"prepay_id=wx05155440194924091e0efc912670185750","signType":"MD5","timeStamp":"1541404480","paySign":"CD4DE57A6B5B2B5418 //输出直接唤起⽀付// $str = "<html><body></body></html>";//// $str .='<html><head><meta http-equiv="content-type" content="text/html;charset=utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><title>微信⽀付</title></head><body>';//// $str .= "<script>function onBridgeReady(){WeixinJSBridge.invoke('getBrandWCPayRequest',$jsApiParameters)}if(typeof WeixinJSBridge=='undefined'){if(document.addEventListener){document.addEventListener('WeixinJSBridgeReady',o//// echo $str;exit;}else{return false;}}/*** 余额⽀付回调* @author ffx**/function amountPayBack(){$xml = file_get_contents('php://input');$xmlJson = json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA));$arr = json_decode($xmlJson, true);//此处注释暂未⽤到/* //⽤户http_build_query()将数据转成URL键值对形式$sign = http_build_query($arr);//md5处理$sign = md5($sign);//转⼤写$sign = strtoupper($sign);//验签名。
干货丨常见的几种扫码支付方式详解干货丨常见的几种扫码支付方式详解来人人都是产品经理【起点学院】,BAT实战派产品总监手把手系统带你学产品、学运营。
目前,以支付宝和微信为代表的扫码支付正在各个线下消费场景中攻城略地,也给我们的生活带来了不小的方便。
今天就来聊聊生活中常见的几种扫码支付方式。
1. 扫码转账支付流程:用户点击支付宝扫一扫,扫码商家事先准备好的二维码,识别出来后选择转账。
扫码分析:此扫码支付的本质其实还是转账,只是通过扫码的方式定位到支付宝账户,然后对其进行转账操作。
类似于扫码对方微信二维码,然后加对方好友。
都是进行对位作用。
费用相关:商家无需承担支付手续费。
优点缺点:优点是无需承担手续费,接入无门槛(无需特定扫码设备、也不需要技术开发);缺点是用户付款后需要进行确认,特别是用户向商家展示付款成功时,商家不可能看的特别清楚,可能会有较低的资金风险。
2. 店铺买单(支付宝)支付流程:用户点击支付宝扫一扫,扫码商家事先准备好的二维码,识别出来后输入消费总金额、不参与优惠金额,并可以享受折扣。
扫码分析:此功能主要应用于加入到支付宝口碑店铺的商家,可以进行促销及优惠券的发放。
费用相关:商家需承担一定的支付手续费。
优点缺点:优点是接入门槛低,支持信用卡、花呗等透支消费,还可以进行常客营销;缺点是需承担手续费,用户付款后需要确认。
3. 公众号扫码支付(微信)支付流程:用户扫描商家准备好的二维码,跳转至商家H5页面,进行下单然后通过微信公众号支付。
扫码分析:实际上此种扫码支付形式只是和扫码发生了点关系而已,其本质是微信公众号支付,只是通过扫码的形式,将用户引导至商家的H5页面,最终进行公众号支付。
费用相关:商家需承担一定的支付手续费。
4. 扫码支付支付流程:微信有2种模式,其中一种与支付宝相同,即先生成预付订单再生成二维码,用户通过扫码二维码对应预付订单进行支付。
这种比较适合交易金额不固定的交易。
微信还有一种模式是,先生成二维码,用户扫码二维码后再生成预付订单并进行支付。
电子支付知识:支付宝和微信支付的使用方法和功能介绍随着移动支付的普及,支付宝和微信支付成为了广大用户生活不可或缺的两个应用。
本文将介绍这两款电子支付的使用方法和功能。
一、支付宝使用方法及功能介绍1.注册登录下载支付宝APP后,注册成为支付宝用户并登录账户,用户可以在支付宝个人中心进行操作。
2.添加银行卡添加银行卡后,就可以进行充值和提现操作。
用户的银行卡信息会被加密保护,安全可靠。
3.扫码支付用户可以通过支付宝扫描商家的支付二维码,进行购买物品的支付。
支付宝支持主流银行和各类优惠活动。
4.转账付款用户可以在支付宝内进行转账,支持绑定支付宝账户、手机号、银行卡等多种方式。
该操作简单便捷,可以在任何时候进行。
5.生活缴费支付宝还支持生活缴费服务,包括水、电、燃气、电话、宽带、有线电视等多项服务。
用户只需添加缴费项目,即可随时进行缴费,不必面对繁琐的缴费手续和排队等待。
6.理财产品支付宝还推出了多种理财产品,用户可根据自身需求进行选择。
理财产品收益高、起投金额低,为用户提供良好的理财方式。
二、微信支付使用方法及功能介绍1.注册登录用户需要下载微信APP并进行注册,才能进行微信支付。
用户登录微信后,在个人中心内即可进行操作。
2.添加银行卡用户需要在微信支付内添加银行卡,以便进行充值和提现操作。
微信支付的充值和提现操作需要使用微信钱包进行。
3.扫码支付微信支付也支持扫码支付,用户可以在微信钱包内扫描商家支付码进行购物。
微信支付支持多种行业和商户,使用范围广泛。
4.转账付款微信支付支持用户进行转账付款操作,用户可以通过微信支付给其他用户转账,并且支持多种付款方式。
5.公共服务微信支付还提供了各种公共服务,包括水电煤气缴费、医院挂号、交通违章查询等多项服务。
用户只需要在微信支付内进行添加服务,即可随时进行操作。
6.理财产品微信支付也推出了丰富的理财产品,包括活期、定期、基金等多种方式。
用户可以根据自身需求进行选择,实现理财增值。
微信支付PHP开发一:微信支付代码及获取支付成功数据一、微信支付demo代码<?phpinclude_once("include/WxPayHelper.php"); //该文件微信DEMO包中有,没有请到官网上下载$commonUtil = new CommonUtil();$wxPayHelper = new WxPayHelper();$user_ip= $_SERVER["REMOTE_ADDR"];$wxPayHelper->setParameter("bank_type", "WX");$wxPayHelper->setParameter("body", "微信支付测试");$wxPayHelper->setParameter("partner", "11111111");$wxPayHelper->setParameter("out_trade_no", $commonUtil->create_noncestr()); $wxPayHelper->setParameter("total_fee", "1");$wxPayHelper->setParameter("fee_type", "1");$wxPayHelper->setParameter("notify_url", "/wxpay/api.php"); $wxPayHelper->setParameter("spbill_create_ip", $user_ip);$wxPayHelper->setParameter("input_charset", "UTF-8");?><html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /><meta content="application/xhtml+xml;charset=UTF-8" http-equiv="Content-Type"><meta content="no-cache,must-revalidate" http-equiv="Cache-Control"><meta content="no-cache" http-equiv="pragma"><meta content="0" http-equiv="expires"><meta content="telephone=no, address=no" name="format-detection"><meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1,minimum-scale=1, width=device-width, height=device-height" /><meta name="apple-mobile-web-app-capable" content="yes" /> <!-- apple devices fullscreen --><meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" /><title>在线支付测试</title></head><script language="javascript">function callpay(){WeixinJSBridge.invoke('getBrandWCPayRequest',<?php echo$wxPayHelper->create_biz_package(); ?>,function(res){if(res.err_msg == "get_brand_wcpay_request:ok" ){// 使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回ok,但并不保证它绝对可靠。
扫码支付API接口开发指南商户接口规版本(3.0.7)修订时间:2017-08-01目录第一章文档描述31、文档说明32、阅读对象33、名词定义3第二章接口定义41、扫码支付42、公众号支付43、支付异步通知104、交易查询125、对账查询14第三章签名说明161、签名算法162、密钥生产16附录1错误码说明17第一章文档描述1、文档说明本说明文档用于指导商户接入扫码支付API系统进行的对接,目前支持支付宝、微信扫码。
请相关技术人员详细阅读本文档。
2、阅读对象商户开发人员。
扫码支付API接口相关技术人员。
3、名词定义合作方:指对接扫码支付平台的机构。
商户:指委托收单的营业机构,如超市、便利店等。
用户:指在商户系统进行消费的企业或者个人。
商户ID:指支付系统为使用外部接入接口的商户统一分配的唯一标识。
商户密钥:指商户在和支付系统进行数据签名认证的密钥,采用RSA加密算法。
第二章接口定义1、扫码支付1、接入URL:118.178.126.35:8088/payservice/pay/smzf2、接入方式:POST方式均可3、请求协议参数:4、返回协议参数:5、接请求提交方法:入{"payType": "ZFBZF","orderId": "WE1124567810","merchantCode": "","totalAmount": "2","subject": "first test","desc": "test product","body": "test product","terminalId": "1213412134","extend1": "test","notifyurl": "118.178.126.35:8088/payservice/pay/notifytestsucc","createip": "192.168.0.1"}返回值:{"rspCode": "000000","rspMsg": "OK","qrCode": "https: //qr.alipay./bax02559bawzwz2erezj00d4","sign":"Ek7H4c6f5OgechohLFrxcgPoGC/vVy0Bg5XDzYGVIReHYaFtEBfSUxhSxCf/rDMhM+DZKJaw5jhT U1mE1ijEQoguj6c6gKDSg6fXuEEwbKODlQDW9cHRFxMVki2THexox/g8KgipUiEW5HOdNNm4Lc wQe8YC+8gauHcKEXSW7Rw="}2、公众号支付1、接入URL:118.178.126.35:8088/payservice/pay/pn2、接入方式:POST方式均可3、请求协议参数:4、返回协议参数:5、接请求提交方法:入{"payType": "ZFBZF","orderId": "WE1124567810","merchantCode": "","totalAmount": "2","subject": "first test","body": "test product","terminalId": "1213412134","extend1": "test","notifyurl": "118.178.126.35:8088/payservice/pay/notifytestsucc","createip": "192.168.0.1"" callbackUrl ": " 118.178.126.35:8088/payservice/pay/callbackurl"" subAppId ": " wxa3dbb6050f553164 "" subOpenId ": "9efi3dbb6050f653164 "}返回值:{"rspCode": "000000","rspMsg": "OK","payCode":{"timeStamp":"74","signType":"MD5","package":"prepay_id=wx2657a0","paySign":"6B0EEA6AE668593A6A290F8247CE46B7","nonceStr":"05571626a6f6415f8cb11c91e64b8450","appId":"wxa3dbb6050f353164"},"sign":"Ek7H4c6f5OgechohLFrxcgPoGC/vVy0Bg5XDzYGVIReHYaFtEBfSUxhSxCf/rDMhM+DZKJaw5jhT U1mE1ijEQoguj6c6gKDSg6fXuEEwbKODlQDW9cHRFxMVki2THexox/g8KgipUiEW5HOdNNm4Lc wQe8YC+8gauHcKEXSW7Rw="}3、支付异步通知1、接入URL:过程1 中请求参数notifyurl值2、接入方式:POST 方式3、通知请求协议参数:4、返回协议参数:5、接入实例:请求提交方法:{"orderid": "WE1124567810","merchantid": "","totalAmount": "2","payTime": "433"," trade_state": "SUCCESS","transcation_id": "BJ55124",}返回值:{"rspCode": "000000","rspMsg": "OK","sign":"Ek7H4c6f5OgechohLFrxcgPoGC/vVy0Bg5XDzYGVIReHYaFtEBfSUxhSxCf/rDMhM+DZKJaw5jhT U1mE1ijEQoguj6c6gKDSg6fXuEEwbKODlQDW9cHRFxMVki2THexox/g8KgipUiEW5HOdNNm4Lc wQe8YC+8gauHcKEXSW7Rw="}6、特别说明:①支付异步通知过程在整个支付流程中一定存在。
扫码支付的报告引言随着互联网和移动支付的快速发展,扫码支付成为了日常生活中非常常见的支付方式之一。
无论是在超市购物、餐馆消费还是线上购物,我们都可以通过扫描二维码轻松完成支付。
本报告将对扫码支付进行深入分析,探讨其发展历程、优势与限制,并展望其未来的发展趋势。
1. 发展历程扫码支付最早起源于中国。
2011年,支付宝推出了支付宝扫码支付服务,用户可以通过支付宝App扫描商家提供的二维码进行支付。
这一创新迅速获得用户的认可,并在短时间内在全国范围内普及。
随着移动支付的普及和技术的进步,各大支付机构纷纷推出了自己的扫码支付服务,如微信支付、银联云闪付等。
2. 扫码支付的优势2.1 方便快捷扫码支付不需要携带现金或刷卡,只需使用手机扫描二维码即可完成支付。
无论是线下还是线上,用户只需简单的几步操作即可完成支付,大大提高了支付的效率和便利性。
2.2 安全可靠扫码支付采用了多种安全技术和加密算法,确保支付过程中的信息安全。
同时,用户在支付过程中无需输入银行卡号等敏感信息,减少了被盗刷的风险。
支付机构也加强了身份验证和风控措施,保障用户的资金安全。
2.3 促进电子支付普及扫码支付为没有银行账户的用户提供了更加便利的支付方式。
无论是小摊小贩还是个体经营者,只需下载一个支付App,即可开始接受扫码支付。
这促进了电子支付的普及和推广,降低了传统支付方式带来的时间成本和人力成本。
3. 扫码支付的限制3.1 依赖互联网和电力扫码支付需要手机网络和电力供应的支持。
在一些偏远地区或网络不稳定的地方,扫码支付可能受到限制。
这对于一些用户而言,可能影响到他们的支付体验。
3.2 隐私和安全问题扫码支付需要用户授权给支付机构访问其个人信息和支付记录。
一些用户担心个人信息的泄露和隐私的保护问题,对于扫码支付持有观望态度。
同时,扫码支付也容易受到黑客攻击和欺诈行为,支付机构需要加强安全措施和风控手段,确保用户资金的安全。
3.3 缺乏标准化目前,扫码支付市场存在多个支付平台和支付方式,导致用户需下载多个App才能完成支付。
微信APP⽀付,⽀付宝APP⽀付demo最近公司新开发的APP中,需要集成微信⽀付和⽀付宝⽀付,2个平台申请的都是APP⽀付。
这是个⼈第⼀次单独的,完整的做完2个平台的⽀付。
这⾥我主要⽤到了2个接⼝:⽀付接⼝,订单查询接⼝,虽然2个平台的⽂档都挺齐全,但开发下来,还是踩了不少坑,记录下来,或许对后来⼈有⽤。
先来说⽀付宝SDK: 了解3个概念:应⽤公钥,应⽤私钥和⽀付宝公钥 开发⼈员⽤⽀付宝提供的秘钥⽣成⼯具⽣成的秘钥,即为应⽤公钥和应⽤私钥。
开发⼈员在给⽀付宝APP配置应⽤公钥后,⽀付宝主动⽣成了⼀个公钥,即为⽀付宝公钥。
配置如下图⽰: 需要把这个⽀付宝公钥保存下来,具体使⽤场景,请看后续说明。
这⾥个⼈⽤到了2个接⼝,⽣成⽀付订单信息和订单查询。
⽀付宝提供了服务端SDK的下载: 其⽬录显⽰: 下⾯直接给出⽀付、回调、订单查询的代码:// ⽣成⽀付信息function pay() {$aop = new AopClient;$aop->gatewayUrl = '⽹关地址';$aop->appId = 'alipay_appid';$aop->rsaPrivateKey = '应⽤私钥';$aop->signType = "RSA2";$aop->alipayrsaPublicKey = '⽀付宝公钥';$request = new AlipayTradeAppPayRequest();$bizcontent = "{\"body\":\"账户充值\",". "\"subject\": \"⽤户账户充值\",". "\"out_trade_no\": \"订单号\",". "\"timeout_express\": \"30m\",". "\"total_amount\": \"订单⾦额\",". "\"product_code\":\"QUICK_MSECURITY_PAY\"". "}";$request->setNotifyUrl('开发者回调地址');$request->setBizContent($bizcontent);$response = $aop->sdkExecute($request);// 注意:官⽅demo⾥,这⾥加了htmlspecialchars⽅法,其实是错误的,应该去掉,否则会报ALI38173错误 echo $response;}// 回调function callback() {$aop = new AopClient ();$aop->alipayrsaPublicKey = '⽀付宝公钥';// ⽀付宝回调是以POST⽅式提交过来的$result = $aop->rsaCheckV1($_POST, null, $_POST['sign_type']);if ($result) {// TODO 验证订单号,⽀付⾦额,⽀付宝状态等信息echo 'success';} else {echo 'error';}}// 订单查询function queryOrder() {$aop = new AopClient ();$aop->signType = "RSA2";$aop->gatewayUrl = '⽹关地址';$aop->appId = 'appid';$aop->rsaPrivateKey = '应⽤私钥';// 注意:这⾥是⽀付宝公钥,不是应⽤公钥$aop->alipayrsaPublicKey = '⽀付宝公钥';$request = new AlipayTradeQueryRequest ();$request->setBizContent("{" ."\"out_trade_no\":\"订单号\"," ."\"trade_no\":\"交易单号\"" ."}");$result = $aop->execute($request);$responseNode = str_replace(".", "_", $request->getApiMethodName()) . "_response";$resultCode = $result->$responseNode->code;if(! empty($resultCode) && $resultCode == 10000) {if ($result->$responseNode->trade_status == 'TRADE_SUCCESS') {// ⽀付成功, 更新订单状态,⽤户账户余额,账户变化⽇志}}}微信⽀付: 在开发平台申请app⽀付: 审核通过后,需要配置开发信息 服务端PHP的demo:// ⽣成⽀付信息function pay() {$input = new WxPayUnifiedOrder();$input->SetBody('⽀付标题');$input->SetAttach('附加信息'); // 附加信息$input->SetOut_trade_no('订单号');$input->SetTotal_fee('订单⾦额,单位分'); //微信⽀付,单位(分)$input->SetTime_start(date("YmdHis"));$input->SetTime_expire(date("YmdHis", time() + 600));$input->SetGoods_tag('附属信息');$input->SetNotify_url('回调地址');$input->SetTrade_type("APP");// ⽣成预⽀付订单ID:prepay_id$unifiedOrder = WxPayApi::unifiedOrder($input);// ⽣成签名,返回给APP前端调⽤$result = makeWxPaySign($unifiedOrder);}// ⽣成签名function makeWxPaySign($UnifiedOrderResult){if(! array_key_exists("appid", $UnifiedOrderResult)|| ! array_key_exists("prepay_id", $UnifiedOrderResult)|| empty($UnifiedOrderResult['prepay_id'])){throw new WxPayException("参数错误");}$api = new WxPayAppPay();$api->SetValue('appid', $UnifiedOrderResult["appid"]);$api->SetValue('package', "Sign=WXPay");$api->SetValue('prepayid', $UnifiedOrderResult['prepay_id']); // 预⽀付订单号$api->SetValue('partnerid', WxPayConfig::MCHID); // 商户号$api->SetValue('noncestr', WxPayApi::getNonceStr()); // 随机数$api->SetValue('timestamp', (string)time());// 对上⾯参数进⾏签名$api->SetValue('sign', $api->MakeSign());return$api->GetValues();}// 回调,继承微信SDK的回调类class PayNotifyCallBack extends WxPayNotify{//重写回调处理函数public function NotifyProcess($data, &$msg){if (! array_key_exists("transaction_id", $data)) {return false;}// 调⽤订单查询⽅法,判断订单真实性return true;}}// 订单查询function queryOrder() {$input = new WxPayOrderQuery();$input->SetTransaction_id('⽀付平台交易号');$input->SetOut_trade_no('公司平台单号');// 订单查询结果$result = WxPayApi::orderQuery($input);if(array_key_exists("return_code", $result)&& array_key_exists("result_code", $result)&& array_key_exists('trade_state', $result)&& $result["return_code"] == "SUCCESS"&& $result["result_code"] == "SUCCESS"&& $result["trade_state"] == "SUCCESS"){// 交易成功,todo 更新订单状态,⽤户账户余额,账户变化⽇志 }}。
PHP微信扫码⽀付DEMO,thinkphp5+微信⽀付PS:微信⽀付基础条件1.⼀个认证的服务号2.在服务号后台申请微信⽀付,成功后获得⼀个商户号3.在商户号⾥⾯选择native⽀付,这个就是扫码⽀付,按照提⽰开通这项。
4.按照微信⽀付⽂档的说法,扫码⽀付分两种模式模式⼀:⽣成的⼆维码没有失效性,但是操作⽐较复杂,中间步骤⽐较多,容易出现很多问题。
模式⼆:⽣成的⼆维码有两⼩时时间限制,但是我感觉也⼗分够⽤了,这个步骤⽐较简单,很容易实现。
按照时序图,⼤概也就分两步代码结构:配置⽂件:微信⽀付类:<?php/** To change this license header, choose License Headers in Project Properties. * To change this template file, choose Tools | Templates* and open the template in the editor.*/namespace tools;use think\Config;/*** Description of WxPay** @author admin*/class WxPay {/*** 获取签名* @param type $arr* @return type*/public function getSign($arr)//去除数组的空值array_filter($arr);if(isset($arr['sign'])){unset($arr['sign']);}//排序ksort($arr);//组装字符$str = $this->arrToUrl($arr) . '&key=' . Config::get('wx_pay')['key'];//使⽤md5 加密转换成⼤写return strtoupper(md5($str));}/*** 校验签名* @param type $arr* @return boolean*/public function checkSign($arr){//⽣成新签名$sign = $this->getSign($arr);//和数组中原始签名⽐较if($sign == $arr['sign']){return true;}else{return false;}}/*** 获取带签名的数组* @param array $arr* @return type*/public function setSign($arr){$arr['sign'] = $this->getSign($arr);return $arr;}/*** 数组转URL字符串不带key* @param type $arr* @return type*/public function arrToUrl($arr){return urldecode(http_build_query($arr));}/*** 记录到⽂件* @param type $file* @param type $data*/public function logs($file,$data){$data = is_array($data) ? print_r($data,true) : $data;file_put_contents('./public/paylogs/' .$file, $data);}/*** 接收POST推送* @return type*/public function getPost(){return file_get_contents('php://input');}/*** Xml ⽂件转数组* @param type $xml* @return string*/public function XmlToArr($xml){if($xml == '') return'';libxml_disable_entity_loader(true);$arr = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);return $arr;}/*** 数组转XML* @param type $arr* @return string*/public function ArrToXml($arr){if(!is_array($arr) || count($arr) == 0) return'';$xml = "<xml>";foreach ($arr as $key=>$val){if (is_numeric($val)){$xml.="<".$key.">".$val."</".$key.">";}else{$xml.="<".$key."><![CDATA[".$val."]]></".$key.">";}}$xml.="</xml>";return $xml;}/*** 发送POST请求* @param type $url* @param type $postfields* @return typepublic function postStr($url,$postfields){$ch = curl_init();$params[CURLOPT_URL] = $url; //请求url地址$params[CURLOPT_HEADER] = false; //是否返回响应头信息$params[CURLOPT_RETURNTRANSFER] = true; //是否将结果返回$params[CURLOPT_FOLLOWLOCATION] = true; //是否重定向$params[CURLOPT_POST] = true;$params[CURLOPT_SSL_VERIFYPEER] = false;//禁⽤证书校验$params[CURLOPT_SSL_VERIFYHOST] = false;$params[CURLOPT_POSTFIELDS] = $postfields;curl_setopt_array($ch, $params); //传⼊curl参数$content = curl_exec($ch); //执⾏curl_close($ch); //关闭连接return $content;}/*** 统⼀下单* @param type $params* @return boolean*/public function unifiedorder($params){//获取到带签名的数组$params = $this->setSign($params);//数组转xml$xml = $this->ArrToXml($params);//发送数据到统⼀下单API地址$data = $this->postStr(Config::get('wx_pay')['uourl'], $xml);$arr = $this->XmlToArr($data);if($arr['result_code'] == 'SUCCESS' && $arr['return_code'] == 'SUCCESS'){return $arr;}else{$this->logs('error.txt', $data);return false;}}}index控制器演⽰代码<?phpnamespace app\index\controller;use app\common\controller\HomeBase;use tools\WxPay;use think\Config;use phpqrcode\ApiQrcode;use think\Request;use think\Cache;use tools\RetJosn;class Index extends HomeBase {/*** ⾸页* @param WxPay $wxpay* @return mixed*/public function index(WxPay $wxpay) {//$wxpay->logs('log.txt',['213123','123123123']);echo 'index';}/*** 扫码⽀付-模式⼆* @param WxPay $wxpay*/public function index2(Request $request, WxPay $wxpay) {if ($request->isGet()) {return $this->fetch();}}/*** 统⼀下单,⽣成⼆维码*/public function getQrUrl(Request $request) {$pid = $request->get('id');//调⽤统⼀下单API$params = ['appid' => Config::get('wx_pay')['appid'],'mch_id' => Config::get('wx_pay')['mchid'],'nonce_str' => md5(time()),'body' => '订单号:'.$pid,'out_trade_no' => $pid,'total_fee' => 2,'spbill_create_ip' => $_SERVER['SERVER_ADDR'],'notify_url' => Config::get('wx_pay')['notify'],'trade_type' => 'NATIVE','product_id' => $pid];$wxpay = new WxPay();$arr = $wxpay->unifiedorder($params);if (!empty($arr['code_url'])) {Cache::set('send' . $params['out_trade_no'], $params['total_fee'], 3600); ApiQrcode::png($arr['code_url']);} else {return'下单失败!';}}/*** 接收腾讯推送⽀付通知* @param WxPay $wxpay*/public function backOrder(WxPay $wxpay) {try {// 获取腾讯传回来的通知数据$xml = $wxpay->getPost();// 将XML格式的数据转换为数组$arr = $wxpay->XmlToArr($xml);$wxpay->logs('logs.txt', '1');// 验证签名if ($wxpay->checkSign($arr)) {Cache::set('back'.$arr['out_trade_no'],$arr['total_fee'],3600);}$wxpay->logs('logs.txt', '2');$params = ['return_code' => 'SUCCESS','return_msg' => 'OK'];echo $wxpay->ArrToXml($params);} catch (\Exception $e) {$wxpay->logs('logs.txt', $e->getMessage());exit();}}/*** 查询⽀付状态* @param Request $request* @return type*/public function checkSuccess(Request $request){$pid = $request->get('id');if (Cache::get('send'.$pid) == Cache::get('back'.$pid)) {return RetJosn::successJson('⽀付成功');} else {return RetJosn::errorJson(Cache::get('send'.$pid).'|'.Cache::get('back'.$pid)); }}}⼆维码页⾯代码:<!DOCTYPE html><!--To change this license header, choose License Headers in Project Properties.To change this template file, choose Tools | Templatesand open the template in the editor.--><html><head><title>TODO supply a title</title><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"></head><body><div class="btn-box"><button onclick="getQrcode()">刷新⼆维码</button></div><div class="qrcode-box"><img id="qrimg" src=""></div><div class="status-box"><h2 id="status"></h2></div></body><script src="__STATIC__/default/js/jquery-1.9.1.min.js"></script><script>(function () {getQrcode();//⼆维码⽣成})();/*验证码⽣成*/function getQrcode() {var id = getId(10);var url = "{:url('Index/getQrUrl')}" + "?id=" + id;$("#qrimg").attr("src", url);sessionStorage.setItem('id',id);checkSuccess();//轮询⽀付状态}/*⽣成唯⼀Id*/function getId(length){var tmp = Date.parse( new Date() ).toString();tmp = tmp.substr(0,length);return tmp;}/*轮询⽀付状态*/function checkSuccess(){var interval = window.setInterval(function(){var id = sessionStorage.getItem('id');$.ajax({url:"{:url('Index/checkSuccess')}?id=" + id,dataType:'json',success:function(res) {if (res.code == 200) {$('#status').html('订单号:'+id+','+res.msg); clearInterval(interval);}}})},2000)}</script></html>效果演⽰:⽣成⼆维码,并开始启动轮询⼿机上⽀付订单:轮询到成功⽀付的状态:。
微信、⽀付宝⼆码合⼀扫码⽀付实现思路(java)⼀、⽀付⼆维码(预订单)根据需要购买的信息创建预订单,将订单信息保存到Redis中,并设置有效期,注意⽣产⼆维码的链接后的参数可以关联到Redis中的key;QRCode 为servlet扫码请求的URL;UUIDUtils.getUUID() 为预订单单号,在servlet请求中截取,然后在Redis中查找对应的Key的数据;⼆、创建⼆维码扫码请求地址servlet:QRCodeServlet;微信⽀付重定向请求servlet:WechatPayServlet;⽀付宝重定向请求servlet:AliPayServlet;QRCodeServlet ⽤于⽤户使⽤微信或者⽀付宝扫码⼆维码进⾏客户端识别及重定向到对应的业务处理;package com.platform.cloudlottery.servlet;import java.io.IOException;import javax.servlet.ServletConfig;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.context.support.SpringBeanAutowiringSupport;import monConfig;import mon.alipay.config.MyAliPayConfig;import mon.wechat.config.WXPublicConfig;import mon.wechat.util.HttpUtil;import com.platform.cloudlottery.model.SysPayChannel;import com.platform.cloudlottery.service.Impl.SysPayChannelServiceImpl;import com.platform.cloudlottery.web.StatusContant.PayTypeConstant;/*** @ClassName: QRCodeServlet* @Description: TODO(根据请求的后缀获取该数据编码对应的数据,并重定向到页⾯)* @author chenkun* @date 2018年12⽉29⽇**/public class QRCodeServlet extends HttpServlet {private static final long serialVersionUID = -8457626626670970403L;protected Logger logger = LoggerFactory.getLogger(getClass());private static final String UrlStr = "QRCode/";private static final String wechatPay = "wechatPay/";private static final String aliPay = "aliPay/";@Autowiredprivate SysPayChannelServiceImpl payChannelService;public void init(ServletConfig servletConfig) throws ServletException {super.init(servletConfig);SpringBeanAutowiringSupport.processInjectionBasedOnServletContext(this, servletConfig.getServletContext());}public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {("####################请求开始####################");String userAgent = request.getHeader("user-agent");String RequestURL = request.getRequestURL().toString();("URL : " + RequestURL);String ReqInfo = RequestURL.substring(RequestURL.indexOf(UrlStr)+UrlStr.length());("URL : " + ReqInfo);CommonConfig commonConfig = new CommonConfig();if (userAgent != null && userAgent.contains("AlipayClient")) {("来⾃⽀付宝");String redirecturi = HttpUtil.urlEnCode(commonConfig.getDomain() + aliPay + ReqInfo);("REDIRECT_URI="+redirecturi);SysPayChannel channel = payChannelService.selectByChannelType(PayTypeConstant.Alipay);MyAliPayConfig aliPayConfig = new MyAliPayConfig();aliPayConfig.setAppId(channel.getAppid());// 授权页⾯地址String requestUrl = aliPayConfig.getAuthgateway();requestUrl = requestUrl.replace("APPID", aliPayConfig.getAppId()).replace("SCOPE", aliPayConfig.getScope()).replace("REDIRECT_URI", redirecturi);// 重定向到授权页⾯response.sendRedirect(requestUrl);} else if (userAgent != null && userAgent.contains("MicroMessenger")) {("来⾃微信");String redirecturi = HttpUtil.urlEnCode(commonConfig.getDomain() + wechatPay + ReqInfo);("REDIRECT_URI="+redirecturi);SysPayChannel channel = payChannelService.selectByChannelType(PayTypeConstant.Wechat);WXPublicConfig publicConfig = new WXPublicConfig();publicConfig.setAppId(channel.getAppid());publicConfig.setOriginId(channel.getOriginid());publicConfig.setAppSecret(channel.getAppsecret());publicConfig.setEncodingAESKey(channel.getEncodingaeskey());// 授权页⾯地址String requestUrl = publicConfig.getAuthorizeinterface();requestUrl = requestUrl.replace("APPID", publicConfig.getAppId()).replace("REDIRECT_URI", redirecturi).replace("SCOPE", publicConfig.getScope()).replace("STATE", publicConfig.getState()); // 重定向到授权页⾯response.sendRedirect(requestUrl);} else {("未知来源");}("####################请求结束####################");}public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {doGet(request, response);}}WechatPayServlet 在获取到Redis中预订单数据后,创建真实订单并调⽤微信“统⼀下单接⼝”;package com.platform.cloudlottery.servlet;import com.alibaba.fastjson.JSONObject;import com.github.wxpay.sdk.WXPayUtil;import monConfig;import mon.jedis.JedisUtil;import ng.StringUtils;import mon.utils.BusinessCodeUtils;import mon.wechat.bean.WeiXinOAuth2Token;import mon.wechat.bean.WeiXinUserInfo;import mon.wechat.config.WXPayConfig;import mon.wechat.config.WXPublicConfig;import mon.wechat.util.WeiXinOAuth2Util;import mon.wechat.util.WeiXinPayUtils;import com.platform.cloudlottery.model.SysPayChannel;import com.platform.cloudlottery.service.Impl.LotteryOrderServiceImpl;import com.platform.cloudlottery.service.Impl.SysPayChannelServiceImpl;import erMemberServiceImpl;import com.platform.cloudlottery.service.OrderServcie;import erInfoService;import com.platform.cloudlottery.web.ResultContant;import com.platform.cloudlottery.web.StatusContant.PayTypeConstant;import com.platform.cloudlottery.web.SysKey;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.annotation.Value;import org.springframework.web.context.support.SpringBeanAutowiringSupport;import redis.clients.jedis.Jedis;import javax.servlet.ServletConfig;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;import java.math.BigDecimal;import java.util.Date;import java.util.LinkedHashMap;import java.util.Map;/*** @ClassName: WechatPayServlet* @Description: TODO(这⾥⽤⼀句话描述这个类的作⽤)* @author chenkun* @date 2019年1⽉5⽇**/public class WechatPayServlet extends HttpServlet {private static final long serialVersionUID = -8457626626670970403L;protected Logger logger = LoggerFactory.getLogger(getClass());private static Jedis redis = JedisUtil.getJedis();@Value("${config.domain}")private String domain;@Value("${config.isProduction}")private boolean isProduction;// 请求路径包含的字符串private static final String UrlStr = "wechatPay/";@Autowiredprivate SysPayChannelServiceImpl payChannelService;@Autowiredprivate UserMemberServiceImpl memberService;@Autowiredprivate LotteryOrderServiceImpl lotteryOrderService;public void init(ServletConfig servletConfig) throws ServletException {super.init(servletConfig);SpringBeanAutowiringSupport.processInjectionBasedOnServletContext(this, servletConfig.getServletContext());}public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {logger.debug("####################请求开始####################");request.setCharacterEncoding("utf-8");response.setCharacterEncoding("utf-8");try {// ⽤户同意授权后,能获取到codeString code = request.getParameter("code");// ⽤户同意授权if (!"authdeny".equals(code)) {CommonConfig commonConfig = new CommonConfig();String RequestURL = request.getRequestURL().toString();logger.debug("URL : " + RequestURL);String QRCodeUrl = RequestURL.substring(RequestURL.indexOf(UrlStr) + UrlStr.length());String QRCodeReqInfo = QRCodeUrl.split("&")[0];String operatorId = QRCodeUrl.split("&")[1];logger.debug("QRCodeReqInfo : " + QRCodeReqInfo +";operatorId : " + operatorId);String advancekey = commonConfig.getLotteryorder() + QRCodeReqInfo;SysPayChannel channel = payChannelService.selectByChannelType(PayTypeConstant.Wechat);WXPublicConfig publicConfig = new WXPublicConfig();publicConfig.setAppId(channel.getAppid());publicConfig.setOriginId(channel.getOriginid());publicConfig.setAppSecret(channel.getAppsecret());publicConfig.setEncodingAESKey(channel.getEncodingaeskey());WXPayConfig payConfig = new WXPayConfig();payConfig.setMchId(channel.getMchid());payConfig.setAppId(channel.getAppid());payConfig.setKey(channel.getWxkey());payConfig.setApicertPath(channel.getPayCertUrl());payConfig.setSpbillCreateIp(channel.getSpbillcreateip());// 获取⽹页授权access_tokenWeiXinOAuth2Token weixinOauth2Token = WeiXinOAuth2Util.getOAuth2AccessToken(publicConfig,code);// ⽹页授权接⼝访问凭证String accessToken = weixinOauth2Token.getAccessToken();logger.debug("accessToken=" + accessToken);// ⽤户标识String openId = weixinOauth2Token.getOpenId();logger.debug("openId="+openId);// 获取⽤户信息WeiXinUserInfo userInfo = WeiXinOAuth2Util.getOAuth2UserInfo(publicConfig, accessToken, openId);logger.debug(userInfo.getNickName()+"=====微信⽀付====="+userInfo.getOpenId());//添加或更新⽤户信息String userid = UserInfoService.CreateUserMember(userInfo,memberService);if (!redis.exists(advancekey)) {// 判断key是否存在logger.debug("⼆维码失效");request.setAttribute("code", ResultContant.notuserdqrcode);request.setAttribute("message", "⼆维码失效");request.getRequestDispatcher("../wechat/WechatPay.jsp").forward(request, response);logger.debug("####################请求结束####################");return;}if (StringUtils.trimToEmpty(redis.get(advancekey)).equals("")) {logger.debug("⼆维码失效");request.setAttribute("code", ResultContant.notuserdqrcode);request.setAttribute("message", "⼆维码失效");request.getRequestDispatcher("../wechat/WechatPay.jsp").forward(request, response);logger.debug("####################请求结束####################");return;}JSONObject jsonObject = JSONObject.parseObject(redis.get(advancekey));if (null == jsonObject) {logger.debug("⼆维码失效");request.setAttribute("code", ResultContant.notuserdqrcode);request.setAttribute("message", "⼆维码失效");request.getRequestDispatcher("../wechat/WechatPay.jsp").forward(request, response);logger.debug("####################请求结束####################");return;}if (redis.get(advancekey + "_lock") != null && !redis.get(advancekey + "_lock").equals("")){logger.debug("⼆维码失效");request.setAttribute("code", ResultContant.notuserdqrcode);request.setAttribute("message", "⼆维码失效");request.getRequestDispatcher("../wechat/WechatPay.jsp").forward(request, response);logger.debug("####################请求结束####################");return;}redis.setex(advancekey + "_lock", 1, "lock");String orderid = BusinessCodeUtils.getOrderNo(jsonObject.getString(SysKey.deviceSn));int money = jsonObject.getIntValue(SysKey.money);int lotterytype = jsonObject.getIntValue(SysKey.lotteryType);if (!orderid.equals("") && money!=0) {//创建订单boolean bool = OrderServcie.createorder(QRCodeReqInfo, PayTypeConstant.Wechat, payConfig.getAppID(), userid, openId, orderid, jsonObject, lotteryOrderService); if (bool) {//删除预订单信息redis.del(advancekey);//⼀个预订单只能创建⼀个订单String paymoney = String.valueOf(money);if (!isProduction) {//测试环境paymoney = BigDecimal.valueOf(Long.valueOf(paymoney)).divide(new BigDecimal(100)).toString();//真实⾦额除100}logger.debug("是否⽣产环境:"+isProduction+";订单⾦额为:"+String.valueOf(money)+";实际⽀付⾦额为:"+paymoney);//创建微信订单Map<String, String> map = WeiXinPayUtils.createOrderJsapi(domain, payConfig, orderid, paymoney, lotterytype==0?"即开票":"电脑票", openId);logger.debug("创建微信⽀付预订单返回数据:"+JSONObject.toJSONString(map));if (map != null) {if (map.get("return_code").equals("SUCCESS")) {if (map.get("result_code").equals("SUCCESS")) {logger.debug("创建微信⽀付预订单成功");Map<String, String> data = new LinkedHashMap<String, String>();data.put("appId", payConfig.getAppID());data.put("timeStamp", String.valueOf(new Date().getTime()/1000));data.put("nonceStr", WXPayUtil.generateNonceStr());data.put("package", "prepay_id="+map.get("prepay_id"));data.put("signType", "MD5");data.put("paySign", WXPayUtil.generateSignature(data, payConfig.getKey()));logger.debug("返回到客户端的数据:"+JSONObject.toJSONString(data));request.setAttribute("appId", data.get("appId"));request.setAttribute("timeStamp", data.get("timeStamp"));request.setAttribute("nonceStr", data.get("nonceStr"));request.setAttribute("package", data.get("package"));request.setAttribute("signType", data.get("signType"));request.setAttribute("paySign", data.get("paySign"));request.setAttribute("code", ResultContant.sucess);request.setAttribute("message", "成功");request.getRequestDispatcher("../wechat/WechatPay.jsp").forward(request, response);}else{logger.debug("创建订单失败:创建⽀付预订单失败");request.setAttribute("code", ResultContant.createordererror);request.setAttribute("message", "创建订单失败");request.getRequestDispatcher("../wechat/WechatPay.jsp").forward(request, response);}} else {logger.debug("创建订单失败:创建⽀付预订单失败");request.setAttribute("code", ResultContant.createordererror);request.setAttribute("message", "创建订单失败");request.getRequestDispatcher("../wechat/WechatPay.jsp").forward(request, response);}} else {logger.debug("创建订单失败:创建⽀付预订单失败");request.setAttribute("code", ResultContant.createordererror);request.setAttribute("message", "创建订单失败");request.getRequestDispatcher("../wechat/WechatPay.jsp").forward(request, response);}} else {logger.debug("创建订单失败:创建⽀付预订单失败");request.setAttribute("code", ResultContant.createordererror);request.setAttribute("message", "创建订单失败");request.getRequestDispatcher("../wechat/WechatPay.jsp").forward(request, response);}} else {logger.debug("创建订单失败;订单⾦额或者订单号数据有误");request.setAttribute("code", ResultContant.createordererror);request.setAttribute("message", "创建订单失败");request.getRequestDispatcher("../wechat/WechatPay.jsp").forward(request, response);}}} catch (Exception e) {e.printStackTrace();logger.debug("系统异常");request.setAttribute("code", ResultContant.notuserdqrcode);request.setAttribute("message", "⼆维码失效");request.getRequestDispatcher("../wechat/WechatPay.jsp").forward(request, response);}logger.debug("####################请求结束####################");}public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response);}}AliPayServlet 在获取到Redis中预订单数据后,创建真实订单并调⽤⽀付宝“⼿机⽹站⽀付接⼝”;package com.platform.cloudlottery.servlet;import com.alibaba.fastjson.JSONObject;import monConfig;import mon.alipay.bean.AliPayOAuth2Token;import mon.alipay.bean.AliPayUserInfo;import mon.alipay.config.MyAliPayConfig;import mon.alipay.uitl.AliPayOAuth2Util;import mon.alipay.uitl.AlipayPayUtils;import mon.jedis.JedisUtil;import ng.StringUtils;import mon.properties.PropertiesUtils;import mon.utils.BusinessCodeUtils;import com.platform.cloudlottery.model.SysPayChannel;import com.platform.cloudlottery.service.Impl.LotteryOrderServiceImpl;import com.platform.cloudlottery.service.Impl.SysPayChannelServiceImpl;import erMemberServiceImpl;import com.platform.cloudlottery.service.OrderServcie;import erInfoService;import com.platform.cloudlottery.web.ResultContant;import com.platform.cloudlottery.web.StatusContant.PayTypeConstant;import com.platform.cloudlottery.web.SysKey;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.annotation.Value;import org.springframework.web.context.support.SpringBeanAutowiringSupport;import redis.clients.jedis.Jedis;import javax.servlet.ServletConfig;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;import java.math.BigDecimal;import java.util.Properties;/*** @ClassName: AliPayServlet* @Description: TODO(这⾥⽤⼀句话描述这个类的作⽤)* @author chenkun* @date 2019年1⽉5⽇**/public class AliPayServlet extends HttpServlet {private static final long serialVersionUID = -8457626626670970403L;protected Logger logger = LoggerFactory.getLogger(getClass());private static Jedis redis = JedisUtil.getJedis();@Value("${config.domain}")private String domain;@Value("${config.isProduction}")private boolean isProduction;// 请求路径包含的字符串private static final String UrlStr = "aliPay/";@Autowiredprivate SysPayChannelServiceImpl payChannelService;@Autowiredprivate UserMemberServiceImpl memberService;@Autowiredprivate LotteryOrderServiceImpl lotteryOrderService;public void init(ServletConfig servletConfig) throws ServletException {super.init(servletConfig);SpringBeanAutowiringSupport.processInjectionBasedOnServletContext(this, servletConfig.getServletContext());}public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { logger.debug("####################请求开始####################");request.setCharacterEncoding("utf-8");response.setCharacterEncoding("utf-8");try {//⽤户同意授权后,能获取到codeString code = request.getParameter("auth_code");//⽤户同意授权if (!code.equals("")) {CommonConfig commonConfig = new CommonConfig();//具体业务String RequestURL = request.getRequestURL().toString();logger.debug("URL : " + RequestURL);String QRCodeUrl = RequestURL.substring(RequestURL.indexOf(UrlStr) + UrlStr.length());String QRCodeReqInfo = QRCodeUrl.split("&")[0];String operatorId = QRCodeUrl.split("&")[1];logger.debug("QRCodeReqInfo : " + QRCodeReqInfo +";operatorId : " + operatorId);String advancekey = commonConfig.getLotteryorder() + QRCodeReqInfo;SysPayChannel channel = payChannelService.selectByChannelType(PayTypeConstant.Alipay);MyAliPayConfig aliPayConfig = new MyAliPayConfig();aliPayConfig.setAppId(channel.getAppid());String certsrc = channel.getPayCertUrl();Properties propertiesFile = PropertiesUtils.getPropertiesFile(certsrc);if (propertiesFile != null) {aliPayConfig.setPayeeAccount(propertiesFile.getProperty("ALI_PAYEE_ACCOUNT"));aliPayConfig.setAppId(propertiesFile.getProperty("ALI_APP_ID"));aliPayConfig.setAliPayPublicKey(propertiesFile.getProperty("ALI_ALIPAY_PUBLIC_KEY"));aliPayConfig.setAppPayPublicKey(propertiesFile.getProperty("ALI_APP_PAY_PUBLIC_KEY"));aliPayConfig.setAppPrivateKey(propertiesFile.getProperty("ALI_APP_PRIVATE_KEY"));}//获取⽹页授权access_tokenAliPayOAuth2Token aliPayOAuth2Token = AliPayOAuth2Util.getOAuth2AccessToken(aliPayConfig,code);//⽹页授权接⼝访问凭证String accessToken = aliPayOAuth2Token.getAccessToken();logger.debug("accessToken=" + accessToken);//⽤户标识String aliuserid = aliPayOAuth2Token.getUserid();logger.debug("aliuserid="+aliuserid);//获取⽤户信息AliPayUserInfo userInfo = AliPayOAuth2Util.getOAuth2UserInfo(aliPayConfig,accessToken,aliuserid);logger.debug(userInfo.getNickName()+"=====⽀付宝⽀付====="+userInfo.getUserId());//添加或更新⽤户信息String userid = UserInfoService.CreateUserMember(userInfo,memberService);if (!redis.exists(advancekey)) {// 判断key是否存在logger.debug("⼆维码失效");request.setAttribute("code", ResultContant.notuserdqrcode);request.setAttribute("message", "⼆维码失效");request.getRequestDispatcher("../alipay/AliPay.jsp").forward(request, response);logger.debug("####################请求结束####################");return;}if (StringUtils.trimToEmpty(redis.get(advancekey)).equals("")) {logger.debug("⼆维码失效");request.setAttribute("code", ResultContant.notuserdqrcode);request.setAttribute("message", "⼆维码失效");request.getRequestDispatcher("../alipay/AliPay.jsp").forward(request, response);logger.debug("####################请求结束####################");return;}JSONObject jsonObject = JSONObject.parseObject(redis.get(advancekey));if (null == jsonObject) {logger.debug("⼆维码失效");request.setAttribute("code", ResultContant.notuserdqrcode);request.setAttribute("message", "⼆维码失效");request.getRequestDispatcher("../alipay/AliPay.jsp").forward(request, response);logger.debug("####################请求结束####################");return;}if (redis.get(advancekey + "_lock") != null && !redis.get(advancekey + "_lock").equals("")){logger.debug("⼆维码失效");request.setAttribute("code", ResultContant.notuserdqrcode);request.setAttribute("message", "⼆维码失效");request.getRequestDispatcher("../alipay/AliPay.jsp").forward(request, response);logger.debug("####################请求结束####################");return;}redis.setex(advancekey + "_lock", 1, "lock");String orderid = BusinessCodeUtils.getOrderNo(jsonObject.getString(SysKey.deviceSn));int money = jsonObject.getIntValue(SysKey.money);int lotterytype = jsonObject.getIntValue(SysKey.lotteryType);if (!orderid.equals("") && money != 0) {//创建订单boolean bool = OrderServcie.createorder(QRCodeReqInfo, PayTypeConstant.Alipay, aliPayConfig.getAppId(), userid, aliuserid, orderid, jsonObject, lotteryOrderService);if (bool) {//删除预订单信息redis.del(advancekey);//⼀个预订单只能创建⼀个订单String paymoney = BigDecimal.valueOf(Long.valueOf(money)).divide(new BigDecimal(100)).toString();if (!isProduction) {//测试环境paymoney = BigDecimal.valueOf(Long.valueOf(paymoney)).divide(new BigDecimal(100)).toString();//真实⾦额除100}logger.debug("是否⽣产环境:"+isProduction+";订单⾦额为:"+BigDecimal.valueOf(Long.valueOf(money)).divide(new BigDecimal(100)).toString()+";实际⽀付⾦额为:"+paymoney); //创建⽀付宝订单String responsestr = AlipayPayUtils.createOrderWapPay(domain, aliPayConfig, orderid, lotterytype==0?"即开票":"电脑票", paymoney, "");logger.debug("创建⽀付宝⽀付预订单返回数据:"+responsestr);if (!responsestr.equals("")) {response.setContentType("text/html;charset=UTF-8");response.getWriter().write(responsestr);//直接将完整的表单html输出到页⾯response.getWriter().flush();response.getWriter().close();response.getWriter().append("Served at: ").append(request.getContextPath());} else {logger.debug("创建订单失败:创建⽀付预订单失败");request.setAttribute("code", ResultContant.createordererror);request.setAttribute("message", "创建订单失败");request.getRequestDispatcher("../alipay/AliPay.jsp").forward(request, response);}} else {logger.debug("创建订单失败:创建⽀付预订单失败");request.setAttribute("code", ResultContant.createordererror);request.setAttribute("message", "创建订单失败");request.getRequestDispatcher("../alipay/AliPay.jsp").forward(request, response);}} else {logger.debug("创建订单失败;订单⾦额或者订单号数据有误");request.setAttribute("code", ResultContant.createordererror);request.setAttribute("message", "创建订单失败");request.getRequestDispatcher("../alipay/AliPay.jsp").forward(request, response);}}} catch (Exception e) {e.printStackTrace();logger.debug("系统异常");request.setAttribute("code", "⼆维码失效");request.setAttribute("message", "⼆维码失效");request.getRequestDispatcher("../alipay/AliPay.jsp").forward(request, response);}logger.debug("####################请求结束####################");}public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {doGet(request, response);}}三、创建微信⽀付结果回调接⼝和⽀付宝⽀付接⼝回调接⼝,⽤于接收微信或者⽀付宝的⽀付结果通知;aliPayNotify ⽀付宝⽀付成功回调接⼝/*** @Title: aliPayNotify* @Description: TODO(⽀付宝⽀付成功过回调)* @author chenkun* @return 参数* @return String 返回类型* @throws*/@RequestMapping(value = "/aliPayNotify", method = RequestMethod.POST)public String aliPayNotify() {ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();HttpServletRequest request = attributes.getRequest();Map<String, String> params = convertRequestParamsToMap(request); // 将异步通知中收到的待验证所有参数都存放到map中String paramsJson = JSON.toJSONString(params);("⽀付宝⽀付回调,{" + paramsJson + "}");try {SysPayChannel channel = payChannelService.selectByChannelType(PayTypeConstant.Alipay);MyAliPayConfig aliPayConfig = new MyAliPayConfig();aliPayConfig.setAppId(channel.getAppid());String certsrc = channel.getPayCertUrl();Properties propertiesFile = PropertiesUtils.getPropertiesFile(certsrc);if (propertiesFile != null) {aliPayConfig.setPayeeAccount(propertiesFile.getProperty("ALI_PAYEE_ACCOUNT"));aliPayConfig.setAppId(propertiesFile.getProperty("ALI_APP_ID"));aliPayConfig.setAliPayPublicKey(propertiesFile.getProperty("ALI_ALIPAY_PUBLIC_KEY"));aliPayConfig.setAppPayPublicKey(propertiesFile.getProperty("ALI_APP_PAY_PUBLIC_KEY"));aliPayConfig.setAppPrivateKey(propertiesFile.getProperty("ALI_APP_PRIVATE_KEY"));}// 调⽤SDK验证签名boolean signVerified = AlipaySignature.rsaCheckV1(params, aliPayConfig.getAliPayPublicKey(),aliPayConfig.getCharset(), aliPayConfig.getSigntype());if (signVerified) {("⽀付宝回调签名认证成功");// 按照⽀付结果异步通知中的描述,对⽀付结果中的业务内容进⾏1\2\3\4⼆次校验,校验成功后在response中返回success,校验失败返回failure this.check(params);// 另起线程处理业务executorService.execute(new AliPayNotifyTask(params,payCallBackService));// 如果签名验证正确,⽴即返回success,后续业务另起线程单独处理// 业务处理失败,可查看⽇志进⾏补偿,跟⽀付宝已经没多⼤关系。
[DEMO] 支付宝网页支付及微信网页扫码支付
DEMO 包含支付宝网页支付以及微信扫码支付功能,相对于手机端支付,完善支付场景,使PC 端与移动端都能实现支付需要。
摘要:
DEMO 已经将支付宝及微信支付需要的配置信息独立化,导入,配置,即可使用。
对于各平台应用前置配置要求,接口各参数意义及用途,请参阅:支付宝网页支付官方文档微信扫码支付(模式二)官方文档
XPZ 下载:
Demo_PCPayment_20180102-114.1.zip (1.04 MB, 下载次数: 1)Note:修复支付宝支付成功页面PaySuccess验签参数SDT 错误
XPZ 下载:
Demo_PCPaymen-t114.2.zip (1.04 MB, 下载次数: 1)另:此xpz 中使用了支付宝服务端sdk,现只实现了.Net 环境的封装,java 环境之后补充。
说明:
导入xpz,配置数据库连接信息,编译运行。
导入module 结构如下:打开访问pcpayment.main.aspx页面
点击“加载配置”按钮可以加载默认配置
按照文档说明配置各参数,点击“保存配置”按钮保存修改
页面下方是用户信息及各用户的支付记录,默认初始化了三个用户用于测试
点击“支付宝支付”按钮,进入支付宝支付页面
默认填入了商品信息及金额,点击页面下方确认支付”按钮进行支付
正常情况下会返回一个form 表单的字符串,及支付宝返回的订单信息
点击“访问上面的返回form 支付”按钮,触发表单中自动提交事件,打开支付宝网页支付页面
完成支付后,将跳转到支付成功页面
返回main 页面查看用户支付记录
可以查看到支付时间,同步返回及异步通知的信息。
可以看到,支付状态一栏中显示了接收到同步及异步信息的先后顺序,且用户关闭支付宝页面后将无法跳转到成功提示页面,及没有同步返回消息,因此,务必按照官方推荐,在异步通知中处理支付成功逻辑。
点击“微信支付 ”按钮,进入微信扫码支付页面
默认填入了订单信息及金额,点击 “确认支付 ”按钮调用微信统一下单 API ,获取 返回参数。
注意微信的金额单位是 “分”,与支付宝 “元”为单位不同。
且此参数不支持小数点, 只能是整数,及 1为最小金额, 0.01元。
正常情况下可以获取到微信支付 url ,点击 “将支付 url
转为二维码进行扫码支付
按钮,获取二维码图片以进行扫码支付
支付完成后返回main 页面查看用户支付记录
可以看到微信没有同步返回参数,依赖于异步通知,业务逻辑同样在异步通知时处理。