精华内容
下载资源
问答
  • 立即付款接口(一堆校验,登录后)2.1视图类2.2序列化类2.3自定义异常类2.4配置文件2.5路由3.立即付款前端4.支付成功get回调用户展示4.1luffycity/ src / views / PaySucess.vue4.2luffycity/ src / router / index.js...

    一.昨日回顾

    1 支付宝支付:
    	-商户号:营业执照(登录的沙箱环境的商家)
    	-客户:我们客户
    2 生成公钥和私钥:公钥配置到支付宝网站上----》支付宝公钥
    	-担心私钥不安全(不要把公钥私钥直接放在代码中,而是放在服务器上)
     	-支付宝支付(官方没有sdk,只提供了api),我们用了第三方,担心有问题
    3 支付流程
    	-用户在前端提交支付--->后端要生成订单(未支付)--->生成支付宝付款链接--->返回前端--->get请求支付链接--->支付宝付款页面(扫描付款)--->支付宝收到钱--->get回调到我们网站(展示用户付款成功)--->post回调(验签)修改订单状态
    	-用户刚打开支付链接,你服务挂了,用户付款成功了
    		-get回调也回不来了
    		-支付宝的post回调,你也收不到(24小时以后,支付宝会推送8)
    			-25小时以内完成8次通知(通知的间隔频率一般是:4m,10m,10m,1h,2h,6h,15h)
    

    二.今日内容

    1.订单表设计

    1 两张表
    	-一个订单可能包含多门课程
    	-订单表:订单号,订单生成时间,订单总价格。。。(订单跟订单详情是一对多的关系)
        -订单详情表:order,course,该课程的价格。。。
    
    from django.db import models
    
    from user.models import User
    from course.models import Course
    # 不同app之间的表,可以建立关联关系,导入使用
    #user = models.ForeignKey(User)  # 不能用引号引起来
    
    class 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)
        # 咱们生成的订单号(唯一:分布式id生成方案:雪花算法,snowflake)
        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="支付方式")
        # 支付时间(付款成功的时候,等支付宝post回调,回掉回来以后,返回数据中有支付时间)
        pay_time = models.DateTimeField(null=True, verbose_name="支付时间")
        user = models.ForeignKey(User, related_name='order_user', on_delete=models.DO_NOTHING, db_constraint=False, verbose_name="下单用户")
        # 订单创建时间
        created_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
    
        class Meta:
            db_table = "luffy_order"
            verbose_name = "订单记录"
            verbose_name_plural = "订单记录"
    
        def __str__(self):
            return "%s - ¥%s" % (self.subject, self.total_amount)
    
        @property
        def courses(self):
            data_list = []
            for item in self.order_courses.all():
                data_list.append({
                    "id": item.id,
                    "course_name": item.course.name,
                    "real_price": item.real_price,
                })
            return data_list
    
    
    class OrderDetail(models.Model):
        """订单详情"""
        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.CASCADE, db_constraint=False, verbose_name="课程")
        price = models.DecimalField(max_digits=6, decimal_places=2, verbose_name="课程原价")
        real_price = models.DecimalField(max_digits=6, decimal_places=2, verbose_name="课程实价")
    
        class Meta:
            db_table = "luffy_order_detail"
            verbose_name = "订单详情"
            verbose_name_plural = "订单详情"
    
        def __str__(self):
            try: # 因为跨表了,可能会出错,所有加异常捕获
                return "%s的订单:%s" % (self.course.name, self.order.out_trade_no)
            except:
                return super().__str__()  # 相当于没写
    

    2.立即付款接口(一堆校验,登录后)

    1 付款接口需要登录后才能使用
    	-自己写jwt的认证类
        -djangorestframwork-jwt 内置有个认证类,+ djangorestframwork的权限类也可以完成认证
        	(目前使用这种)
    
    2 记得更改lib/al_pay/pay.py
    这个网址后面记得加'?'
    # 支付网关
    GATEWAY = 'https://openapi.alipaydev.com/gateway.do?' if DEBUG else 'https://openapi.alipay.com/gateway.do?'
    

    2.1视图类

    from rest_framework_jwt.authentication import JSONWebTokenAuthentication
    from rest_framework.permissions import IsAuthenticated
    from rest_framework.viewsets import GenericViewSet
    from rest_framework.mixins import CreateModelMixin
    from .models import Order
    from .serializer import PayOrderSerializer
    from utils.response import APIResponse
    class PayView(GenericViewSet,CreateModelMixin):
        queryset = Order.objects.all()
        serializer_class = PayOrderSerializer
        # 认证类,需要搭配权限类--->保证必须登录才能用
        # 只使用下面一个:(JSONWebTokenAuthentication)
        # 用户可以登录,也可以不登录,
        # 如果登录了后续能之间获取request.user
        # 如果没有登录,就获取不到
        authentication_classes = [JSONWebTokenAuthentication,]
        permission_classes = [IsAuthenticated,]
    
        # 重写create,控制返回的数据
        def create(self, request, *args, **kwargs):
            ser=self.get_serializer(data=request.data,context={'request':request})
            ser.is_valid(raise_exception=True)
            #保存
            self.perform_create(ser)
            # ser.save()  跟跟上面一样
    
            pay_url=ser.context.get('pay_url')
    
            return APIResponse(msg='订单创建成功',pay_url=pay_url)
    
    

    2.2序列化类

    from rest_framework import serializers
    from course.models import Course
    from rest_framework.exceptions import ValidationError
    from utils.exception import MyException
    from .models import Order,OrderDetail
    import uuid
    from django.conf import settings
    from lib.al_pay import alipay,gateway
    class PayOrderSerializer(serializers.ModelSerializer):
        # 用户传入的是courses:[1,2,3]==转成课程对象==》[obj1,obj2,obj3]
        # courses:[1,2,3]---->PrimaryKeyRelatedField--->指定queryset,会去queryset中过滤,主键为[1,2,3]的课程
        courses = serializers.PrimaryKeyRelatedField(queryset=Course.objects.all(), many=True, write_only=True)
    
        # user=serializers.PrimaryKeyRelatedField(queryset=User.objects.all(),many=False)
        class Meta:
            model = Order
            # 从前端传过来(订单号后端生成)
            '''
              {'subject':'python从入门到精通',
                total_amount:100,
                pay_type:1 (支付类型,目前只有支付宝),
                courses:[1,2,3] 课程id号
             }
            '''
            fields = ['subject', 'total_amount', 'pay_type', 'courses']
            extra_kwargs = {
                'total_amount': {'required': True, }
            }
    
        def _check_price(self, attrs):
            courses = attrs.get('courses')
            total = 0
            for course in courses:
                total += course.price
            if not total == attrs.get('total_amount'):
                raise MyException('钱数不合法')
    
        def _get_order_no(self):
            order_no=str(uuid.uuid4())
            return order_no.replace('-','')
    
        def _get_user(self):
            user=self.context.get('request').user
            return user
    
        def _gen_pay_url(self,attrs,order_no):
    
            order_string = alipay.api_alipay_trade_page_pay(
                out_trade_no=order_no,
                total_amount=float(attrs.get('total_amount')), # 转成浮点型
                subject=attrs.get('subject'),
                # 写在配置文件中
                return_url=settings.RETURN_URL,
                notify_url=settings.NOTIFY_URL
            )
            return gateway+order_string
    
        def _pre_create(self,attrs,user,order_no):
            # 把用户,订单状态,订单号,写入attrs
            attrs['order_status']=0
            attrs['user']=user
            attrs['out_trade_no']=order_no
    
    
    
        def validate(self, attrs):
            # 1)订单总价校验
            self._check_price(attrs)
            # 2)生成订单号
            order_no=self._get_order_no()
            # 3)支付用户:request.user
            user=self._get_user()
            # 4)支付链接生成
            pay_url=self._gen_pay_url(attrs,order_no)
            self.context['pay_url']=pay_url
            # 5)入库(两个表)的信息准备(重写create方法)
            self._pre_create(attrs,user,order_no)
            return attrs
    
        def create(self, validated_data):
            # 存两个表
            # course需要存detail表
            courses=validated_data.pop('courses')
            # 开启一个事务
            order=Order.objects.create(**validated_data)
            for course in courses:
                OrderDetail.objects.create(order=order,course=course,price=course.price,real_price=course.price)
    
            return order
    
    

    2.3自定义异常类

    '''=========>utils/exception.py'''
    # 自定义异常类
    from rest_framework.exceptions import  APIException
    
    class MyException(APIException):
        status_code = 405
        default_detail = '校验出错了'
        default_code = '错误'
    
        def __init__(self, detail=None, code=None):
            super().__init__(detail=None, code=None)
    

    2.4配置文件

    '''========>settings/dev.py'''
    # 支付宝回调地址
    
    RETURN_URL = "http://127.0.0.1:8080/course/paysucess"
    NOTIFY_URL = "http://127.0.0.1:8000/"  # 必须要用公网
    

    2.5路由

    # 使用相对导入
    from . import views
    # 自动生成路径
    from rest_framework.routers import SimpleRouter
    from .views import PayView
    router = SimpleRouter()
    # 127.0.0.1:8000/order/pay/
    router.register('pay',PayView,'PayView')
    
    from .views import PayView
    urlpatterns = [
        # path('pay', PayView.as_view()),
    ]
    urlpatterns+=router.urls  # 方式二
    

    3.立即付款前端

    views/ActualCourse.vue

    buy_now(course) {
        //没有登录,提示请先登录
        let token = this.$cookies.get('token')
        if (token) {
            //发送请求获取支付链接
            this.$axios({
                    method: 'post',
                    url: this.$settings.base_url + '/order/pay/',
                    data: {
                        subject: course.name,
                        total_amount: course.price,
                        pay_type: 1,
                        courses: [course.id,]
                    },
                    headers: {
                        authorization: 'jwt ' + token
                    }
                }
            ).then(item => {
                console.log(item.data)
                if (item.data.status == 0) {
                    open(item.data.pay_url, '_self')
                } else {
                    this.$message({
                        message: item.data.msg
                    })
                }
            })
    
        } else {
            this.$message({
                message: "请先登录"
            })
        }
    }
    

    4.支付成功get回调用户展示

    4.1luffycity/ src / views / PaySucess.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: {},
                };
            },
            created() {
                // url后拼接的参数:?及后面的所有参数 => ?a=1&b=2
                // 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);
                    if(!response.data.status==0){
                        console.log('暂未收到您的付款,请3s钟后再刷新该页面')
                    }
                }).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>
    

    4.2luffycity/ src / router / index.js

    {
            path: '/course/paysucess',
            name: 'PaySucess',
            component: PaySucess,
        }
    

    4.3get回调参数

    # get回调,携带一些参数过来,展示课程购买成功
    charset=utf-8&
    out_trade_no=fe2baf0893d1403a89773e1e0151b4b3&
    method=alipay.trade.page.pay.return&total_amount=99.00&
    sign=C%2BtM1IqF9QBB7N86m6sEkJDoe8nvGKEymOPst%2FhrOTqZvZdyRTbr37a%2BAkhjV6Co6ot64mwJDVvlraJqevYltjXvWNovcViYAXL3JNZ%2FUoOo91PIsgFMJsTgXSy2R%2FyQ7NAhRhIhxGhNs5JNzTLt2JINKcZ%2FiUxzM%2Bkz3Z1EbjgB0JaDoNgRs9Wpwqb1VT%2FwZnyOAuoxOBwhij2SeP2ZC5qZfjQ8gzglSiUzbrQplbT3ZCGu5NHE1h42Zs8r3PkyxhghoK2T8UC7suI2u7l94713L8vP5hnegxkB79fNd4DQEZ4hnTV5nWDXjXw5RP9ob%2FQthMCTGssPS9%2Flme0%2F7w%3D%3D&
    trade_no=2021011522001453300501032429&
    auth_app_id=2016092000554611&version=1.0&
    app_id=2016092000554611&
    sign_type=RSA2&seller_id=2088102176466324&
    timestamp=2021-01-15%2012%3A23%3A44
    

    4.4后端接口

    '''=====>order/urls.py'''
    from .views import PaySuccess
    path('success', PaySuccess.as_view()),
    '''=====>order/views.py'''
    class PaySuccess(APIView):
        def get(self,request,*args,**kwargs): # 咱们前端回调的
            out_trade_no=request.GET.get('out_trade_no')
            # 去数据库查询该订单是否已经支付完成
            order=Order.objects.filter(out_trade_no=out_trade_no,order_status=1).first()
            if order: # post回调回调完了,订单状态改了
                return APIResponse(msg='支付成功')
            else:
                return APIResponse(status=1,msg='暂未支付成功')
    

    5.支付成功post回调修改订单状态

    # lib/al_pay/init.py
    from .pay import alipay,gateway
    
    # order/views.py
    from rest_framework.views import APIView
    from utils.logging import get_logger
    logger=get_logger('pay')
    
    class PaySuccess(APIView):
    
        def post(self,request,*args,**kwargs): # 支付宝回调
            # 验签,通过,修改订单状态,返回给支付宝success
            try:
                result_data = request.data.dict()  # 一定不能忘
                out_trade_no = result_data.get('out_trade_no')
                signature = result_data.pop('sign') # 签名,必须验证签名
                from lib import al_pay
                result = al_pay.alipay.verify(result_data, signature)
                if result and result_data["trade_status"] in ("TRADE_SUCCESS", "TRADE_FINISHED"):
                    # 完成订单修改:订单状态、流水号、支付时间
                    Order.objects.filter(out_trade_no=out_trade_no).update(order_status=1)
                    # 完成日志记录
                    logger.warning('%s订单支付成功' % out_trade_no)
                    return Response('success')  # 必须返回给支付宝,否则支付宝会一直回调
                else:
                    logger.error('%s订单支付失败' % out_trade_no)
            except:
                pass
            return Response('failed')
    
    展开全文
  • 文章目录关于微信商户企业付款给用户限额问题的说明相关阅读 1、对于商户 2、对于客户 3、申请提额 4、解决方案 - 企业付款给银行卡 关于微信商户企业付款给用户限额问题的说明 @ 如果觉得本文对你有帮助,可以...

    文章目录

    • 关于微信商户企业付款给用户限额问题的说明
      • 相关阅读
          • 1、对于商户
          • 2、对于客户
          • 3、申请提额
          • 4、解决方案 - 企业付款给银行卡

    展开全文
  • 与OA系统集成,例如:申请对供应商AP发票付款,在财务(出纳)节点对供应商付款和填写付款信息及审批以后,某节点审批通过以后,或者审批完成,自动同步付款信息至Oracle EBS 测试成功的Oracle EBS版本

    背景

    在某项目中,需要把其他系统的AP发票和付款同步至Oracle EBS系统。但是,Oracle官方并没有提供标准AP付款API,需要自行客制化开发AP付款API,
    其中,它主要用应场景:

    1. 与资金管理系统集成: 例如:在资金系统中,对供应商的付款以后,需要同步到Oracle EBS.
    2. 与OA系统集成,例如:申请对供应商AP发票付款,在财务(出纳)节点对供应商付款和填写付款信息及审批以后,某节点审批通过以后,或者审批完成,自动同步付款信息至Oracle EBS

    快速参考

    参考点 内容
    功能导航 N: Payables Manager->Payments->Entry->Payments
    并发请求 None
    基表 AP_CHECKS_ALL
    AP_INVOICE_PAYMENTS_ALL
    API 参考下面介绍
    错误信息表 None
    接口Form None
    接口Report None
    Debug Profile None
    详细例子 None
    官方文档 None
    数据验证包
    展开全文
  • 【准备工作】在准备着手开发之前呢,我建议大家先去查阅一下微信的 APP支付开发者文档 ,对微信支付开发的...商户后台收到用户支付单,调用微信支付统一下单接口。参见 【统一下单API】统一下单接口返回正常的prepay...

    【准备工作】

    在准备着手开发之前呢,我建议大家先去查阅一下微信的 APP支付开发者文档 ,对微信支付开发的流程有一个系统的了解。

    我这里为大家准备了一张交互时序图,以便大家随时查看:

    9cda2dd44f98

    image

    APP支付时序图

    商户系统和微信支付系统主要交互说明:

    用户在商户APP中选择商品,提交订单,选择微信支付。

    商户后台收到用户支付单,调用微信支付统一下单接口。参见 【统一下单API】

    统一下单接口返回正常的prepay_id,再按签名规范重新生成签名后,将数据传输给APP。参与签名的字段名为appId,partnerId,prepayId,nonceStr,timeStamp,package。注意:package的值格式为Sign=WXPay

    商户APP调起微信支付。api参见本章节 【app端开发步骤说明】

    商户后台接收支付通知。api参见 【支付结果通知API】

    商户后台查询支付结果。api参见 【查询订单API】

    【着手开发】

    由于我们是做服务端,因此我们更关注服务端数据的处理,因此,跳过第一步。不过我们还是要先来模拟一些订单数据:

    点击步骤2中的统一下单API的链接,我们可以看到我们请求接口时需要向其传输的一些参数,包括应用ID、商户号、设备号等等,我们只需向其传输必填项即可,选填数据可以根据自己的实际需求来决定。

    9cda2dd44f98

    image

    商品信息数据

    appid 和 mch_id 分别去到微信开放平台和微信商户平台中获取,nonce_str (随机字符串) 很随意了,不长于32位就好。

    /**

    * 生成随机数并返回

    */

    private function getNonceStr() {

    $code = "";

    for ($i=0; $i > 10; $i++) {

    $code .= mt_rand(1000); //获取随机数

    }

    $nonceStrTemp = md5($code);

    $nonce_str = mb_substr($nonceStrTemp, 5,37); //MD5加密后截取32位字符

    return $nonce_str;

    }

    [图片上传中...(image-bbd938-1554388876603)]

    notify_url(通知地址)是接收微信支付异步通知回调地址,通知url必须为直接可访问的url,不能携带参数。例如:'https://pay.weixin.qq.com/wxpay/pay.action'

    接下来是微信支付中最为关键的步骤之一:签名,微信支付整个流程下来一共要经过三次签名。

    9cda2dd44f98

    image

    签名算法

    上图所示的是微信签名的算法说明,特别要注意图上我所标识出来的关键点。根据微信官方的签名算法,我编写了下面的方法:

    /**

    * 获取参数签名;

    * @param Array 要传递的参数数组

    * @return String 通过计算得到的签名;

    */

    private function getSign($params) {

    ksort($params); //将参数数组按照参数名ASCII码从小到大排序

    foreach ($params as $key => $item) {

    if (!empty($item)) { //剔除参数值为空的参数

    $newArr[] = $key.'='.$item; // 整合新的参数数组

    }

    }

    $stringA = implode("&", $newArr); //使用 & 符号连接参数

    $stringSignTemp = $stringA."&key=".$this->key; //拼接key

    // key是在商户平台API安全里自己设置的

    $stringSignTemp = MD5($stringSignTemp); //将字符串进行MD5加密

    $sign = strtoupper($stringSignTemp); //将所有字符转换为大写

    return $sign;

    }

    注意:key的值长度不能超过32位。

    这里,我们最好编写一个类文件来包含这些方法,比如上面我们获取签名的方法会重复调用很多次,写在类方法里能减少耦合,并且方便多次调用。

    那么我编写了一个微信支付的类文件(我会在文章的最末尾将源码提供给大家参考),该类在实例化的同时会初始化一些固定数据,例如appid 、 mch_id 等

    /**

    * 构造函数,初始化成员变量

    * @param String $appid 商户的应用ID

    * @param Int $mch_id 商户编号

    * @param String $key 秘钥

    */

    // 将构造函数设置为私有,禁止用户实例化该类

    private function __construct($appid, $mch_id, $key) {

    if (is_string($appid) && is_string($mch_id)) {

    $this->appid = $appid;

    $this->mch_id = $mch_id;

    $this->key = $key;

    }

    }

    /**

    * 获取微信支付类实例

    * 该类使用单例模式

    * @return WeEncryption 本类实例

    */

    public static function getInstance() {

    if(self::$instance == null) {

    self::$instance = new Self(APPID, MCHID, APP_KEY);

    }

    return self::$instance;

    }

    再调用 WeEncryption::setNotifyUrl($url) 方法来设置异步通知回调地址:

    /** * 设置通知地址 * @param String $url 通知地址; */public function setNotifyUrl($url) { if (is_string($url)) { $this->notify_url = $url; }}

    直到现在,我们所有需要向统一下单接口传输的数据已经全部准备完毕了,接下来就该向微信请求数据了。

    首先我们先将要发送的数据拼装成xml格式:

    /** * 拼装请求的数据 * @return String 拼装完成的数据 */private function setSendData($data) { $this->sTpl = ""; //xml数据模板 $nonce_str = $this->getNonceStr(); //调用随机字符串生成方法获取随机字符串 $data['appid'] = $this->appid; $data['mch_id'] = $this->mch_id; $data['nonce_str'] = $nonce_str; $data['notify_url'] = $this->notify_url; $data['trade_type'] = $this->trade_type; //将参与签名的数据保存到数组 // 注意:以上几个参数是追加到$data中的,$data中应该同时包含开发文档中要求必填的剔除sign以外的所有数据 $sign = $this->getSign($data); //获取签名 $data = sprintf($this->sTpl, $this->appid, $data['body'], $this->mch_id, $nonce_str, $this->notify_url, $data['out_trade_no'], $data['spbill_create_ip'], $data['total_fee'], $this->trade_type, $sign); //生成xml数据格式 return $data;}

    特别注意:

    xml数据要使用注释包裹

    到此,我们的准备工作已经完毕,可以开始向统一下单接口发起请求了:

    /** * 发送下单请求; * @param Curl $curl 请求资源句柄 * @return mixed 请求返回数据 */public function sendRequest(Curl $curl, $data) { $data = $this->setSendData($data); //获取要发送的数据 $url = "https://api.mch.weixin.qq.com/pay/unifiedorder"; $curl->setUrl($url); //设置请求地址 $content = $curl->execute(true, 'POST', $data); //执行该请求 return $content; //返回请求到的数据}

    以上示例代码中包含了一个Curl类,是一个数据请求工具类,不了解的小伙伴可以百度查一下。该工具主要是帮助我们发送请求用的,稍后我会在文章的最后将该类文件的源码跟微信支付类一起展示给大家。

    我们在客户端代码中实例化该工具类,调用 WeEncryption::sendRequest(Curl

    math?formula=curl%2Cdata) 方法请求下单接口:

    $curl = new Curl(); //实例化传输类;$xml_data = $encpt->sendRequest($curl, $data); //发送请求

    我们已经向下单接口发送请求,如果请求成功,微信会向我们返回一些数据:

    9cda2dd44f98

    image

    返回数据

    好的,此时我们开始第三步 —— 二次签名。

    我们重点关注一下返回数据中的 prepay_id,该参数是微信生成的预支付回话标识,用于后续接口调用中使用,该值有效期为2小时。

    在第三步中我们得知:

    统一下单接口返回正常的prepay_id,再按签名规范重新生成签名后,将数据传输给APP。

    参与签名的字段名为appId,partnerId,prepayId,nonceStr,timeStamp,package。

    上一步我们向微信发送请求后,我们将返回的数据保存到了变量$xml_data中,接下来,我们根据上一步微信返回的数据判断上一次的请求是否成功:

    $postObj = $encpt->xmlToObject($xml_data); //解析返回数据 if ($postObj === false) { echo 'FAIL'; exit; // 如果解析的结果为false,终止程序} if ($postObj->return_code == 'FAIL') { echo $postObj->return_msg; // 如果微信返回错误码为FAIL,则代表请求失败,返回失败信息;} else { //如果上一次请求成功,那么我们将返回的数据重新拼装,进行第二次签名 $resignData = array( 'appid' => $postObj->appid, 'partnerId' => $postObj->mch_id, 'prepayId' => $postObj->prepay_id, 'nonceStr' => $postObj->nonce_str, 'timeStamp' => time(), 'package' => 'Sign=WXPay' ); //二次签名; $sign = $encpt->getClientPay($resignData); echo $sign;}

    上述代码中,我们先调用了 WeEncryption::xmlToObject($xml_data) 方法解析返回数据:

    /** * 解析xml文档,转化为对象 * @author 栗荣发 2016-09-20 * @param String $xmlStr xml文档 * @return Object 返回Obj对象 */public function xmlToObject($xmlStr) { if (!is_string($xmlStr) || empty($xmlStr)) { return false; } // 由于解析xml的时候,即使被解析的变量为空,依然不会报错,会返回一个空的对象,所以,我们这里做了处理,当被解析的变量不是字符串,或者该变量为空,直接返回false $postObj = simplexml_load_string($xmlStr, 'SimpleXMLElement', LIBXML_NOCDATA); $postObj = json_decode(json_encode($postObj)); //将xml数据转换成对象返回 return $postObj;}

    如果返回数据无误,接着将重新参与签名的数据拼装好,进行二次签名,在这里我需要提醒一下大家:

    package的值为Sign=WXPay不变

    时间戳使用time()获取就好

    mch_id 即为 partnerId

    其他数据可以使用微信返回的数据,也可以自己写

    最重要的一点,看下图:

    9cda2dd44f98

    image

    注意事项

    图中微信说参与签名的字段包含这些,我圈起来的变量是大小写结合的,但实际上,二次签名的时候所有的变量都是小写的,否则会提示签名错误(这一点坑了我好久)。

    最后调用 WeEncryption::getClientPay($data) 重新生成签名

    /** * 获取客户端支付信息 * @author 栗荣发 2016-09-18 * @param Array $data 参与签名的信息数组 * @return String 签名字符串 */public function getClientPay($data) { $sign = $this->getSign($data); // 生成签名并返回 return $sign;}

    将重新生成的签名传输给 APP 客户端。

    返回的时候,要将sign,appId,partnerId,prepayId,nonceStr,timeStamp,package 这七个值一起返回个 APP 客户端。

    【验签】

    完成前面我们讲解的过程之后,APP客户端已经可以调起微信的支付界面进行支付了,但是整个过程还没有完成。为了用户资金的安全起见,防止数据被篡改,我们要对微信返回过来的数据进行验证。

    还记得我们上一次向微信发送请求的时候,我们填写了一个 notify_url 的参数吗?当APP客户端请求支付成功后,微信会发起一个并行操作:

    向APP客户端返回支付状态

    向商户后台服务器返回支付结果

    我们先来获取一下微信向商户后台服务器返回的结果:

    /** * 接收支付结果通知参数 * @return Object 返回结果对象; */public function getNotifyData() { $postXml = $GLOBALS["HTTP_RAW_POST_DATA"]; // 接受通知参数; if (empty($postXml)) { return false; } $postObj = $this->xmlToObject($postXml); // 调用解析方法,将xml数据解析成对象 if ($postObj === false) { return false; } if (!empty($postObj->return_code)) { if ($postObj->return_code == 'FAIL') { return false; } } return $postObj; // 返回结果对象;}

    然后我们在客户端代码中接收一下:

    $obj = $encpt->getNotifyData(); // 接收数据对象

    然后重新拼装数据准备第三次签名:

    if ($obj) { $data = array( 'appid' => $obj->appid, 'mch_id' => $obj->mch_id, 'nonce_str' => $obj->nonce_str, 'result_code' => $obj->result_code, 'openid' => $obj->openid, 'trade_type' => $obj->trade_type, 'bank_type' => $obj->bank_type, 'total_fee' => $obj->total_fee, 'cash_fee' => $obj->cash_fee, 'transaction_id' => $obj->transaction_id, 'out_trade_no' => $obj->out_trade_no, 'time_end' => $obj->time_end ); // 拼装数据进行第三次签名 $sign = $encpt->getSign($data); // 获取签名 /** 将签名得到的sign值和微信传过来的sign值进行比对,如果一致,则证明数据是微信返回的。 */ if ($sign == $obj->sign) { $reply = ""; echo $reply; // 向微信后台返回结果。 exit; }}

    你以为到这里整个流程就结束了?看起来是可以了,支付已经完成了,而且我们也已经向微信发送了成功消息,还有什么要做的吗?

    答案是肯定的,接下来APP客户端还会我们发起请求查询实际结果。

    试想:如果遇到突发情况,我们的服务器没有接收到来自微信的通知消息,那我们没有返回给微信任何消息,结果是失败的,但是APP客户端却收到了微信返回的支付成功的通知,遇见这种情况我们该怎么办?

    因此,当支付流程结束后,我们的APP客户端依然要向我们发起一个请求,查询实际的订单状态,此时我们需要客户端将订单号传递给我们,然后我们使用订单号,继续向微信发起请求:

    /** * 查询订单状态 * @param Curl $curl 工具类 * @param string $out_trade_no 订单号 * @return xml 订单查询结果 */public function queryOrder(Curl $curl, $out_trade_no) { $nonce_str = $this->getNonceStr(); $data = array( 'appid' => $this->appid, 'mch_id' => $this->mch_id, 'out_trade_no' => $out_trade_no, 'nonce_str' => $nonce_str ); $sign = $this->getSign($data); $xml_data = '%s%s%s%s%s'; $xml_data = sprintf($xml_data, $this->appid, $this->mch_id, $nonce_str, $out_trade_no, $sign); $url = "https://api.mch.weixin.qq.com/pay/orderquery"; $curl->setUrl($url); $content = $curl->execute(true, 'POST', $xml_data); return $content;}

    获取到查询结果后,我们可以根据微信的返回值来判断实际的支付结果。

    在这一步,我们也可以在确保成功后,将订单的信息保存到数据库。

    【结束】

    至此,整个支付流程已经结束了,希望可以对大家有所帮助,有什么问题可以在下方留言。

    展开全文
  • API 接口防刷

    2021-02-27 15:48:55
    API 接口防刷顾名思义,想让某个接口某个人在某段时间内只能请求N次。在项目中比较常见的问题也有,那就是连点按钮导致请求多次,以前在web端有表单重复提交,可以通过token 来解决。除了上面的方法外,前后端配合的...
  • 付款方式◆ 支持API接口或网页操作,付款至目标用户。收款用户身份指定◆ 通过APPID+OPENID指定收款用户。◆ APPID需要为申请商户号时的APPID,或者与商户号有绑定关系。◆ OPENID的获取方式,可参照:...
  • 1、微信支付的几种接口付款码支付、JSAPI支付、Native支付、APP支付、H5支付、小程序支付。几种支付方式都是大同小异。2、支付流程基于APP支付来说,微信支付的大体流程:1、APP客户端根据用户支付请...
  • 【实例简介】H5微信支付H5微信支付手写源码【实例截图】【核心代码】namespace weixinpayApp;...class wxh5{//$data 金额和订单号public function wxh5Request($data){$appid = 'wxdf************';...
  • Oracle EBS 银行账户API

    2021-05-06 01:15:35
    创建银行-- Create BankDECLAREp_init_msg_list VARCHAR2(200);p_country_code VARCHAR2(200);p_bank_name VARCHAR2(200);p_bank_number VARCHAR2(200);p_alternate_ban...
  • 涉及的Java知识有:interface(接口),abstract(抽象修饰符),extends(继承),implements(接口的实现),@Override(重写)案例具体要求:要求实现银行卡系统的银联接口银联接口,用于描述银联统一制定的规则,该接口...
  • 以下针对企业和个体工商户才可以申请的,如果个人想申请的话 您转到网址... 8 验证完成之后,微信支付整个流程算是走完了,如果需要开通支付通道,这就需要获取相应的接口,然后进行对接就可以了。
  • 首先,下面是调用支付宝接口的官网: 支付宝开放平台https://open.alipay.com/platform/home.htm我们这里只演示沙箱环境下的,正式环境需要审核什么的,正式环境与此配置类似,因此不必在意。 进入官网后,首先...
  • 支付宝支付接口开通流程

    千次阅读 2021-03-25 12:31:41
    需要开通的支付产品: 电脑网站支付:C扫B,用户通过手机扫描系统生成的支付二维码 旅行社、自助机(C扫B模式,没有扫描器) 当面付:B扫C,景区通过扫码枪等设备扫描用户出示的付款码 人工窗口、手持机、自助机(B...
  • 第三方支付接口有哪些? 目前中国国内的第三方支付产品主要有支付宝(阿里巴巴旗下)、微信(腾讯公司)、QQ钱包(财付通公司)、云闪付(中国银联旗下)等。其中最用户数量最大的是支付宝和微信。 第三方支付...
  • 微信有一个API是企业付款,可以从企业账户付款到个人微信的零钱,另外还支持付款到银行卡,其实这个功能可以用于普通用户在商户端提现功能的开发。...课程大纲课时1:微信企业付款到零钱接口分析与实现课时...
  • 调用微信企业付款提示“此IP地址不允许调用接口,如有需要请登录微信支付商户平台更改配置”。 配置步骤: 1.登录微信商户,进入产品中心,以企业付款到零钱为例,点击产品设置: 2.找到付款发起方式,点击设置...
  • 通过web3.js提供的web3对象,我们可以很方便地和以太坊交互,web3.js是以太坊提供的javascript库,它封装了以太坊的JSON PRC API,所以web3.js可以与任何暴露了RPC接口的以太坊区块链节点连接。在前面的章节中,我们
  • 微信支付接口

    2021-04-21 20:00:49
    微信调用jssdk接口 wx.config({ debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。 ...
  • 毕设支付接口免签

    2021-07-04 07:16:03
    个人支付免签系统 Api 版 项目说明: 支持个人网站、安卓App、微信公众号、Pc软件收款的接入,所有的资金都会实时到账您的支付宝/微信余额中,支付宝无需上传收款二维码,支持H5唤醒支付,支持回调通知、支持补单、...
  • //like12 add,20211126,预留测试接口,price=999999900 if("999999900".equals(price)){//应交费 九百九十九万 九千九百九十九 price = "1";//强制改为1分钱 } //#######################################...
  • 开发流程业务流程说明:1、用户在商户侧完成下单,使用微信支付进行支付2、由商户后台向微信支付发起下单请求(调用统一下单接口)注:交易类型trade_type=MWEB3、统一下单接口返回支付相关参数给商户后台,如支付跳转...
  • 一 微信支付分对接(java)

    千次阅读 2021-02-28 16:54:42
    需要注意的点 接口调用使用微信支付API v3 相较于的之前微信支付API,主要区别是: ·遵循统一的Restful的设计风格 ·使用JSON作为数据交互的格式,不再使用XML ·使用基于非对称密钥的SHA256-RSA的数字签名算法,...
  • 加载证书请求接口 System.out.println(data); result = weChatWithdrawal(data); System.out.println("------aaaaa------"+result); } catch (Exception e) { e.printStackTrace(); return "银行卡提现失败"; } ...
  • JavaWeb - 接口幂等性

    2021-01-22 15:53:00
    接口调用存在的问题 现如今我们的系统大多拆分为分布式SOA,或者微服务,一套系统中包含了多个子系统服务,而一个子系统服务往往会去调用另一个服务,而服务调用服务无非就是使用RPC通信或者restful,既然是通信,...
  • php微信公众号开发之微信企业付款给个人来源:中文源码网浏览: 次日期:2019年11月5日【下载文档:php微信公众号开发之微信企业付款给个人.txt】(友情提示:右键点上行txt文档名->目标另存为)php微信公众号开发之...
  • 3、API证书 微信支付接口中,涉及资金回滚的接口会使用到API证书,包括退款、撤销接口。商家在申请微信支付成功后,收到的相应邮件后,可以按照指引下载API证书,也可以按照以下路径下载:微信商户平台(pay.weixin....
  • 在我的项目中还没有接入这个功能的时候就听说微信支付的接口很坑爹,用完之后发现别人说的一点也没有错,蛋疼归蛋疼,该用还是要用。在这里把遇到的问题记录一下以免以后需要再次接入的话碰到问题浪费时间。微信支付...
  • 前面做了app微信支付的回调处理,现在...首先,我们贴出调用支付接口的H5页面,当然,在这个页面之前,还需要做很多其他的操作,我们一步一步的来。坑爹的官方文档给了两个不同的支付接口,在微信公众平台开发中文档...
  • NOT NULL COMMENT '付款金额', `ipn_track_id` varchar(50) COLLATE utf8mb4_general_ci NOT NULL, `protection_eligibility` varchar(50) COLLATE utf8mb4_general_ci NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=...
  • 微信支付通用支付接口一 原理二 请求参数三 支付结果 一 原理 因微信扫码支付的特殊性,因此其无法提供地址重定向功能,需要我们自己编写 websocket 来实现功能,所以特地提供一个已经继承了 websocket 的支付页面和...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 34,322
精华内容 13,728
关键字:

付款接口