• 微信第三方平台开发接入授权流程接步骤如下:(微信https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1453779503&token=&...

    微信第三方平台开发接入

    授权流程接入步骤如下:(

    微信https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1453779503&token=&lang=zh_CN

    1、接收微信服务器推送的component_verify_ticket
    2、根据微信服务器推动的component_verify_ticket获取第三方平台component_access_token
    3、根据component_access_token获取预授权码pre_auth_code
    4、给客户端提供获取授权页网址接口
    5、用户授权后接收通知(参考9)
    6、根据授权码换取公众号或小程序的接口调用凭据和授权信息(获取authorizer_access_token和authorizer_refresh_token)
    7、根据authorizer_refresh_token刷新公众号或小程序的接口调用凭据(authorizer_access_token会过期)
    8、获取授权公众号或小程序基本信息

    9、接收推送授权相关通知(当公众号或小程序对第三方进行授权、取消授权、更新授权时,将通过事件推送告诉开发者)


    接入中遇到的问题():

    1、预授权码已经被授权过,不能重复使用

        获取预授权码是没有请求限制次数的,所以每一次进行网页授权的时候请求新的授权码。预授权码不要重复使用,每次授权码的过期时间是600s也就是说600s内一个授权码只能供一个用户进行公众号授权。

    2、微信公众号授权给第三方平台时报“没有绑定公众号”

        没有全网发布的第三方平台,需要添加公众号ID(gh_xxxxx之类的)到第三方平台测试帐号里才可以授权,否则需要申请全网发布。


    3、{"errcode":40119,"errmsg":"invalid use button type hint: [T80906vr30]"

    通过各种验证,授权给第三方平台的公众号若是没有创建菜单的权限,此时使用authorizer_access_token创建自定义菜单时,会出现以上错误。


    展开全文
  • 用java做带薪开发,首先就要先通过微信接入配置,大概原理就是微信往服务器发送一些数据,然后在服务器端做一些数据的处理,再返回数据就行了。 微信官方的名称是: 验证服务器地址的有效性 下面是微信官方的...

    微信上接入平台的demo是PHP的,没得java的,所以我自己做接入的时候遇到了一点小问题。用java做带薪开发,首先就要先通过微信的接入配置,大概原理就是微信往服务器发送一些数据,然后在服务器端做一些数据的处理,再返回数据就行了。

    微信官方的名称是:

    验证服务器地址的有效性

    下面是微信官方的文档:

    开发者提交信息后,微信服务器将发送GET请求到填写的服务器地址URL上,GET请求携带四个参数:

    参数 描述
    signature 微信加密签名,signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数。
    timestamp 时间戳
    nonce 随机数
    echostr 随机字符串

    开发者通过检验signature对请求进行校验(下面有校验方式)。若确认此次GET请求来自微信服务器,请原样返回echostr参数内容,则接入生效,成为开发者成功,否则接入失败。

    加密/校验流程如下:
    1. 将token、timestamp、nonce三个参数进行字典序排序
    2. 将三个参数字符串拼接成一个字符串进行sha1加密
    3. 开发者获得加密后的字符串可与signature对比,标识该请求来源于微信
    实例开始

    1:写个servlet类,来处理微信发送过来的数据:
    package com.levi.service;
    
    import java.io.IOException;
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.Comparator;
    import java.util.List;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import com.levi.utils.SHA1;
    
    /**
     * Servlet implementation class WeiXin
     */
    public class WeiXin extends HttpServlet {
    	private static final long serialVersionUID = 1L;
    	 private static String Token = "leviweixin";  //这个是之前在微信上填写的Token数据,可以自定义
        /**
         * @see HttpServlet#HttpServlet()
         */
        public WeiXin() {
            super();
            // TODO Auto-generated constructor stub
        }
    
    	/**
    	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
    	 */
    	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    		 System.out.println("get请求,正确");
    		System.out.println("获得微信请求!");
    		String signature=request.getParameter("signature");
    		String timestamp=request.getParameter("timestamp");
    		String nonce=request.getParameter("nonce");
    		String echostr=request.getParameter("echostr");
    		System.out.println("signature="+signature);
    		System.out.println("timestamp="+timestamp);
    		System.out.println("nonce="+nonce);
    		System.out.println("echostr="+echostr);
    		System.out.println("Token="+Token);
    		
    		 List<String> params = new ArrayList<String>();  
             params.add(Token);  
             params.add(timestamp);  
             params.add(nonce);  
             //1. 将token、timestamp、nonce三个参数进行字典序排序  
             Collections.sort(params, new Comparator<String>() {  
                 @Override  
                 public int compare(String o1, String o2) {  
                     return o1.compareTo(o2);  
                 }  
             }); 
             //2. 将三个参数字符串拼接成一个字符串进行sha1加密  
             String temp = SHA1.encode(params.get(0) + params.get(1) + params.get(2));  
             if (temp.equals(signature)) {  
            	 System.out.println("原:"+signature);
            	 System.out.println("测试:"+temp);
            	 System.out.println("匹配正确,传回微信了");
                 response.getWriter().write(echostr);  
                 System.out.println("传回微信成功");
             }  
    		 else {  
            System.out.println("没有传回去数据,");
         } 
    	}
    
    	/**
    	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
    	 */
    	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    	 System.out.println("post请求,错误了");
    	}
    
    }
    

    2:需要一个SHA1 的util类:
    /*
     * 微信公众平台(JAVA) SDK
     *
     * Copyright (c) 2016, Ansitech Network Technology Co.,Ltd All rights reserved.
     * http://www.ansitech.com/weixin/sdk/
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *      http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    package com.levi.utils;
    
    import java.security.MessageDigest;
    
    /**
     * <p>Title: SHA1算法</p>
     *
     * @author qsyang<yangqisheng274@163.com>
     */
    public final class SHA1 {
    
        private static final char[] HEX_DIGITS = {'0', '1', '2', '3', '4', '5',
                               '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
    
        /**
         * Takes the raw bytes from the digest and formats them correct.
         *
         * @param bytes the raw bytes from the digest.
         * @return the formatted bytes.
         */
        private static String getFormattedText(byte[] bytes) {
            int len = bytes.length;
            StringBuilder buf = new StringBuilder(len * 2);
            // 把密文转换成十六进制的字符串形式
            for (int j = 0; j < len; j++) {
                buf.append(HEX_DIGITS[(bytes[j] >> 4) & 0x0f]);
                buf.append(HEX_DIGITS[bytes[j] & 0x0f]);
            }
            return buf.toString();
        }
    
        public static String encode(String str) {
            if (str == null) {
                return null;
            }
            try {
                MessageDigest messageDigest = MessageDigest.getInstance("SHA1");
                messageDigest.update(str.getBytes());
                return getFormattedText(messageDigest.digest());
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }
    
    3、配置下web.xml就行了
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
      <display-name>wx</display-name>
      <welcome-file-list>
        <welcome-file>index.html</welcome-file>
        <welcome-file>index.htm</welcome-file>
        <welcome-file>index.jsp</welcome-file>
        <welcome-file>default.html</welcome-file>
        <welcome-file>default.htm</welcome-file>
        <welcome-file>default.jsp</welcome-file>
      </welcome-file-list>
      <servlet>
        <description></description>
        <display-name>WeiXin</display-name>
        <servlet-name>WeiXin</servlet-name>
        <servlet-class>com.levi.service.WeiXin</servlet-class>
      </servlet>
      <servlet-mapping>
        <servlet-name>WeiXin</servlet-name>
        <url-pattern>/WeiXin</url-pattern>
      </servlet-mapping>
    </web-app>



    点击提交,如果正确会显示配置成功。

    注意事项:
    1、一定要用到外网IP映射,可以用花生壳,或者用ngrok。端口要用80端口。用外网映射的目的就是模拟服务器,然后微信才能够给服务器发信息并且验证;
    2、要多连续点击几次提交,直到ok为止,我测试的时候就发现,明明是对的,但是微信就说配置失败,后面我多次连续点击提交,非常快的速度连续点击提交。有个前提,要代码没写错哈,其实你把上面的3个代码块的文件复制进去应该是没得问题的。

    展开全文
  • 前段时间在一个App中接入微信支付功能,想来也稳定运行快一个月了,回头想想自己接入微信支付时候踩过的坑,决定写一篇自己当初想要搜寻的文章,文章准备分3篇完成。 第一篇:微信支付前期准备 第二篇:微信...

    前段时间在一个App中接入了微信支付功能,想来也稳定运行快一个月了,回头想想自己接入微信支付时候踩过的坑,决定写一篇自己当初想要搜寻的文章,文章准备分3篇完成。
    第一篇:微信支付前期准备
    第二篇:微信支付接入客户端部分(以Android为例)
    第三篇:微信支付接入服务端部分

    微信支付支持多种支付方式,具体可以查看微信支付官网。本文将讲解APP支付
    这里写图片描述

    注册App相关

    APP支付接入方法指引,开通微信支付必须是企业账号,个人开发者不可以使用

    第一步:注册开放平台账号
    登录开放平台open.weixin.qq.com,注册成为微信开放平台开发者。

    第二步:认证开放平台并创建APP
    开放平台需进行开发者资质认证后才可申请微信支付,认证费:300元/次(注意是每次啊,万一衰神附体第一次没有过,那再来一次就再交300大洋);
    提交APP基本信息,通过开放平台应用审核,以获得AppID。

    第三步:提交资料申请微信支付
    登录开放平台,点击【管理中心】,选择需要申请支付功能对应的APP,开始填写资料等待审核,审核时间为1-5个工作日内。

    第四步:开户成功,登录商户平台进行验证
    资料审核通过后,请登录联系人邮箱查收商户号和密码,并登录商户平台填写财付通备付金打的小额资金数额,完成账户验证。(查看验证方法)

    第五步:在线签署协议
    本协议为线上电子协议,签署后方可进行交易及资金结算,签署完立即生效。点此提前预览协议内容。

    第六步:启动设计和开发
    支付接口已获得,可根据开发文档进行开发,也可了解成功案例界面示意及素材。

    说的非常清楚,认证的时候会有人会向你注册时候填写的银行卡里面转入金额很小的一笔钱(几毛钱),然后打电话问你钱的金额,一共打两次钱,记不清是不是打两次电话了。然后成功后就会发一封邮件给你,里面有一些信息,你通过官网或者微信按照要求完成账户验证即可。

    APP支付

    相信App的支付场景我无需赘言,由于两个马爸爸的强势推广,中国现在大部分人对APP支付都不会陌生。

    在接下来最好看一下官方开发文档,我刚开始看得时候看不太懂,也觉得没什么屌用,可是等到费了九牛二虎之力搞定后,才领悟到开发文档的重要性。如果刚开始看不懂没有关系,在开发过程中你需要不断的回看的,接下来我挑重点说一下。

    • 业务流程模块一定要看,就是下面这张图

    这里写图片描述

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

    步骤1:用户在商户APP中选择商品,提交订单,选择微信支付。
    步骤2:商户后台收到用户支付单,调用微信支付统一下单接口。
    步骤3:统一下单接口返回正常的prepay_id,再按签名规范重新生成签名后,将数据传输给 APP。参与签名的字段名为appid,partnerid,prepayid,noncestr,timestamp,package。
    步骤4:商户APP调起微信支付。
    步骤5:商户后台接收支付通知。
    步骤6:商户后台查询支付结果。

    这张图涵盖了微信APP支付的精华,照着以上步骤开发即可,接下来要解决的其实就是每一步如何做的问题。

    预知后事如何,且看下篇博文

    展开全文
  • 自己开发一套微信支付接口(SDK)是子恒老师《子恒说微信开发》视频教程的第15部。详细讲解了用php开发一套自己的微信支付接口。内容包含微信支付开发思路,基础类开发,微信支付接口应用,公众号发红包,企业付款等等...
  • 首先,我简单总结一下这几天搞微信支付和支付宝支付接口的体会,通过这几天的...做微信支付接口接入,官方文档负责参考所需传递的参数,Demo完全没必要去下(鸡肋),觉得最靠谱的还是在Github找一个有注释的完整D...

    首先,我简单总结一下这几天搞微信支付和支付宝支付接口的体会,通过这几天的动手实操,加强了自己对SSM框架的认识,以及对业务逻辑的思路,明白了许多以前不懂的许多空白。
    做支付宝接口的接入完全可以按照支付宝官方所给的Demo和文档,稍加整合即可获得自己想要的代码实现。
    做微信支付接口接入,官方文档负责参考所需传递的参数,Demo完全没必要去下(鸡肋),觉得最靠谱的还是在Github找一个有注释的完整Demo自己细细品味,而且微信的沙箱环境想要用的话还要注册一堆东西,真的特别零散。
    不废话了。。。。直接放图来看:
    根据应用场景选择实现模式
    Native支付可分为两种模式,商户根据支付场景选择相应模式。
    【模式一】:商户后台系统根据微信支付规则链接生成二维码,链接中带固定参数productid(可定义为产品标识或订单号)。用户扫码后,微信支付系统将productid和用户唯一标识(openid)回调商户后台系统(需要设置支付回调URL),商户后台系统根据productid生成支付交易,最后微信支付系统发起用户支付流程。
    商户支付回调URL设置指引:进入商户平台–>产品中心–>开发配置,进行配置和修改,如图6.6所示。

    图6.6 Native支付回调URL设置
    设置完成后,详细接入步骤:模式一
    【模式二】:商户后台系统调用微信支付【统一下单API】生成预付交易,将接口返回的链接生成二维码,用户扫码后输入密码完成支付交易。注意:该模式的预付单有效期为2小时,过期后无法支付。详细接入步骤:模式二
    上面微信支付的业务逻辑和上一篇博客所提到的支付宝支付逻辑大同小异,无非不用的是支付宝支付,跳转的是支付宝统一的支付页面,而微信支付发起支付是生成一个二维码传给你的服务器,再由你展示给客户,当客户完成支付时再由微信分别异步通知给服务器和客服。
    我们来看一下这个项目的整个目录结构:
    1.是Controller层,前端的所有逻辑都要通过该层,
    2.是微信支付的整个逻辑,里面有config(参数配置),util(工具类),service和seviceimpl(业务逻辑实现层),entitys(实体类),当发起一个微信支付请求的时候,Controller层的方法直接调用serviceimpl业务实现层的方法生成一个url由前端拼接到二维码上。
    3.是一个开源的Sid包,用来生成独一无二的订单号id
    4.是springmvc的配置文件
    5.是一个前端的管理工具叫做bower,有兴趣的可以自己去百度
    6.是前端所有的页面
    OK,我们通过一次支付实现来完成整个项目的演示:
    在上面的框输入这个地址访问后端Controller的AlipayController的alipy/products方法通过该方法返回一个前端页面products.jsp页面,在该页面显示我们的产品列表,点击购买后跳转到后端Controller的AlipayController的alipay/goConfirm方法,然后通过该方法在返回一个ModelAndView,返回至goConfirm.jsp页面
    我们在这里填写想要购买的个数后,点击ajax提交订单还是回跳转到刚才的Controller的alipay/goPay方法里面,再返回至goPay页面:
    在这里有两种支付方式我们点击 微信扫码支付,这时前端通过form表单提交订单的相关信息到这个:
    在这个Controller层会调用
    在这个方法里面会生成向微信支付接口递交的相关信息,生成Xml格式的信息通过下面这个工具类提交到微信支付后台的服务器上,微信也会给我们返回一个Xml格式的数据,里面包括了用户要扫码的Url地址:
    然后再由刚才的Controller层将这个Url传给前端:
    通过前端JQuery提供的一个二维码的生成方法,来显示它:
    最后显示的页面如下:
    到此!整个支付接口已经全部开发基本算完毕,再需要的是我们要考虑网络传输的安全性,代码能否经受住实际环境的考验,因为我并没有申请微信商家号,所有这一份功能还需要大家自己完善。
    在这里给出sql语句,在自己的Mysql上运行即可:
    /*
    SQLyog Ultimate v12.5.1 (64 bit)
    MySQL - 5.7.22 : Database - alipay


    /
    /
    !40101 SET NAMES utf8 /;
    /
    !40101 SET SQL_MODE=’’/;
    /
    !40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 /;
    /
    !40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 /;
    /
    !40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE=‘NO_AUTO_VALUE_ON_ZERO’ /;
    /
    !40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 /;
    CREATE DATABASE /
    !32312 IF NOT EXISTS*/alipay /*!40100 DEFAULT CHARACTER SET utf8mb4 */;
    USE alipay;
    /*Table structure for table flow */
    DROP TABLE IF EXISTS flow;
    CREATE TABLE flow (
    id varchar(20) NOT NULL,
    flow_num varchar(20) DEFAULT NULL COMMENT ‘流水号’,
    order_num varchar(20) DEFAULT NULL COMMENT ‘订单号’,
    product_id varchar(20) DEFAULT NULL COMMENT ‘产品主键ID’,
    paid_amount varchar(11) DEFAULT NULL COMMENT ‘支付金额’,
    paid_method int(11) DEFAULT NULL COMMENT ‘支付方式\r\n 1:支付宝\r\n 2:微信’,
    buy_counts int(11) DEFAULT NULL COMMENT ‘购买个数’,
    create_time datetime DEFAULT NULL COMMENT ‘创建时间’,
    PRIMARY KEY (id)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT=‘流水表’;
    /*Data for the table flow */
    /*Table structure for table orders */
    DROP TABLE IF EXISTS orders;
    CREATE TABLE orders (
    id varchar(20) NOT NULL,
    order_num varchar(20) DEFAULT NULL COMMENT ‘订单号’,
    order_status varchar(20) DEFAULT NULL COMMENT ‘订单状态\r\n 10:待付款\r\n 20:已付款’,
    order_amount varchar(11) DEFAULT NULL COMMENT ‘订单金额’,
    paid_amount varchar(11) DEFAULT NULL COMMENT ‘实际支付金额’,
    product_id varchar(20) DEFAULT NULL COMMENT ‘产品表外键ID’,
    buy_counts int(11) DEFAULT NULL COMMENT ‘产品购买的个数’,
    create_time datetime DEFAULT NULL COMMENT ‘订单创建时间’,
    paid_time datetime DEFAULT NULL COMMENT ‘支付时间’,
    PRIMARY KEY (id)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT=‘订单表’;
    /*Data for the table orders */
    insert into orders(id,order_num,order_status,order_amount,paid_amount,product_id,buy_counts,create_time,paid_time) values
    (‘1812207R22F6DM14’,‘1812207R22F6DM14’,‘10’,‘2.0’,NULL,‘1’,2,‘2018-12-20 10:51:46’,NULL),
    (‘1812207S7CN814M8’,‘1812207S7CN814M8’,‘10’,‘2.0’,NULL,‘1’,2,‘2018-12-20 10:55:21’,NULL),
    (‘1812207S87PPY79P’,‘1812207S87PPY79P’,‘10’,‘2.0’,NULL,‘1’,2,‘2018-12-20 10:55:26’,NULL),
    (‘1812208CY53HRRYW’,‘1812208CY53HRRYW’,‘10’,‘34.0’,NULL,‘1’,34,‘2018-12-20 11:51:24’,NULL),
    (‘1812208HW44KTDYW’,‘1812208HW44KTDYW’,‘10’,‘34.0’,NULL,‘1’,34,‘2018-12-20 12:03:11’,NULL),
    (‘1812208KD75YTKGC’,‘1812208KD75YTKGC’,‘10’,‘34.0’,NULL,‘1’,34,‘2018-12-20 12:05:01’,NULL),
    (‘181220ASYCGYY428’,‘181220ASYCGYY428’,‘10’,‘2.0’,NULL,‘1’,2,‘2018-12-20 15:09:35’,NULL),
    (‘181220ASZY889K1P’,‘181220ASZY889K1P’,‘10’,‘2.0’,NULL,‘1’,2,‘2018-12-20 15:09:45’,NULL),
    (‘181220GM4PMR5CDP’,‘181220GM4PMR5CDP’,‘10’,‘20.0’,NULL,‘1’,20,‘2018-12-20 21:55:36’,NULL),
    (‘1812219SF42HKSY8’,‘1812219SF42HKSY8’,‘10’,‘2.0’,NULL,‘1’,2,‘2018-12-21 13:44:12’,NULL),
    (‘1812219SFG49P18H’,‘1812219SFG49P18H’,‘10’,‘2.0’,NULL,‘1’,2,‘2018-12-21 13:44:15’,NULL),
    (‘181221DCZZMB5PZC’,‘181221DCZZMB5PZC’,‘10’,‘2.0’,NULL,‘1’,2,‘2018-12-21 18:51:56’,NULL),
    (‘181221DGKAR7CD1P’,‘181221DGKAR7CD1P’,‘10’,‘2000.0’,NULL,‘1’,2000,‘2018-12-21 18:59:49’,NULL),
    (‘181221FGG6X50PH0’,‘181221FGG6X50PH0’,‘10’,‘253.0’,NULL,‘1’,253,‘2018-12-21 20:23:39’,NULL);
    /*Table structure for table product */
    DROP TABLE IF EXISTS product;
    CREATE TABLE product (
    id varchar(20) NOT NULL,
    name varchar(20) DEFAULT NULL COMMENT ‘产品名称’,
    price varchar(11) DEFAULT NULL COMMENT ‘价格’,
    PRIMARY KEY (id)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='产品表 ';
    /*Data for the table product */
    insert into product(id,name,price) values
    (‘1’,‘辣条’,‘1.0’);
    /*Table structure for table user */
    DROP TABLE IF EXISTS user;
    CREATE TABLE user (
    id varchar(20) NOT NULL,
    username varchar(128) DEFAULT NULL,
    sex varchar(20) DEFAULT NULL,
    PRIMARY KEY (id)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT=‘用户表’;
    /*Data for the table user /
    /
    !40101 SET SQL_MODE=@OLD_SQL_MODE /;
    /
    !40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS /;
    /
    !40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS /;
    /
    !40111 SET SQL_NOTES=@OLD_SQL_NOTES */;

    加裙 柳丝零3583儿儿 有很多老师不定时帮大家讲解 也有大公司一些大佬 每天分享想工作经验 有兴趣的小伙伴可以加 群文件有老师上传的资料有用到的自己下

    展开全文
  • 这段时间在给一个iOS应用程序开发一个微信支付接口,之前并没有接触过微信支付,翻阅了一上午的微信开发文档,对其流程做了大致的了解后,便开始着手该接口程序的编写。 编写的过程中也遇到了各种各样的问题,各方...

     

    PHP开发APP微信支付接口

    转载 :

    这段时间在给一个iOS应用程序开发一个微信支付接口,之前并没有接触过微信支付,翻阅了一上午的微信开发文档,对其流程做了大致的了解后,便开始着手该接口程序的编写。 编写的过程中也遇到了各种各样的问题,各方查找资料,终于完成了该接口的开发。想到以后小伙伴们肯能会用的到,因此,我将整个接口的开发流程分享给大家。

    【准备工作】

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

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

    APP支付时序图

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

    1. 用户在商户APP中选择商品,提交订单,选择微信支付。
    2. 商户后台收到用户支付单,调用微信支付统一下单接口。参见 【统一下单API】
    3. 统一下单接口返回正常的prepay_id,再按签名规范重新生成签名后,将数据传输给APP。参与签名的字段名为appId,partnerId,prepayId,nonceStr,timeStamp,package。注意:package的值格式为Sign=WXPay
    4. 商户APP调起微信支付。api参见本章节 【app端开发步骤说明】
    5. 商户后台接收支付通知。api参见 【支付结果通知API】
    6. 商户后台查询支付结果。api参见 【查询订单API】

    【着手开发】

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

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

    商品信息数据

    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;
    }

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

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

    签名算法

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

    
    /**
     * 获取参数签名;
     * @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>
                            <appid><![CDATA[%s]]></appid>
                            <body><![CDATA[%s]]></body>
                            <mch_id><![CDATA[%s]]></mch_id>
                            <nonce_str><![CDATA[%s]]></nonce_str>
                            <notify_url><![CDATA[%s]]></notify_url>
                            <out_trade_no><![CDATA[%s]]></out_trade_no>
                            <spbill_create_ip><![CDATA[%s]]></spbill_create_ip>
                            <total_fee><![CDATA[%d]]></total_fee>
                            <trade_type><![CDATA[%s]]></trade_type>
                            <sign><![CDATA[%s]]></sign>
                        </xml>";                          //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数据要使用<![CDATA[]]>注释包裹

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

    
    /**
     * 发送下单请求;
     * @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 $curl, $data) 方法请求下单接口:

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

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

    返回数据

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

    我们重点关注一下返回数据中的 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
    • 其他数据可以使用微信返回的数据,也可以自己写
    • 最重要的一点,看下图:

    注意事项

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

    最后调用 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客户端请求支付成功后,微信会发起一个并行操作:

    1. 向APP客户端返回支付状态
    2. 向商户后台服务器返回支付结果

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

    
    /**
     * 接收支付结果通知参数
     * @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 = "<xml>
                        <return_code><![CDATA[SUCCESS]]></return_code>
                        <return_msg><![CDATA[OK]]></return_msg>
                    </xml>";
            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 = '<xml>
                       <appid>%s</appid>
                       <mch_id>%s</mch_id>
                       <nonce_str>%s</nonce_str>
                       <out_trade_no>%s</out_trade_no>
                       <sign>%s</sign>
                    </xml>';
        $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;
    }

    获取到查询结果后,我们可以根据微信的返回值来判断实际的支付结果。
    在这一步,我们也可以在确保成功后,将订单的信息保存到数据库。

    【结束】

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

    展开全文
  • 最近为公司开发一个新功能,具体是什么不重要,主要是这个功能需要对接微信支付,需要在微信页面调起微信支付 基本上我也是初次接触微信支付的对接,所以我先去看了微信的支付开发文档地址如下: ...在这里我采用的是JSAPI来...
  • 本套课程的设计完全是为初学者量身打造,课程内容由浅入深,课程讲解通俗易懂,代码实现简洁清晰。通过本课程的学习,学员能够入门微信公众平台开发,能够胜任企业级的订阅号、服务号、企业号的应用开发工作。 ...
  • 发现网上能够参考的资料基本上很少,很多地方都讲的不够详细,致使许多新手采坑无数,所以这篇文章讲一下如何使用laravel接入微信接口,实现微信公众号二次开发,顺便也会穿插一些laravel基础知识的讲解。...
  • 在上一篇中,我们讲解了如何申请微信公众号,公众平台开发的基本原理以及服务器环境的搭建。这一讲开始,我们正式进入微信公众号的开发。 1.微信接口 我们前一篇已经讲了,微信平台(不管是订阅号还是服务号)...
  • 一、这是官方文档的接入讲解 这儿前面第一步就不说了,配置自己的服务器,我这儿没有服务器,用的是ngrok内网穿透(问题也不知道是不是这个产生的原因,下面会讲)。 这儿第二步就是微信接口验证了,看官方文档说...
  • 课程讲解了Unity如何接入微信登录 微信分享 微信支付 支付宝支付
  • 首先去申请开通微信支付功能(亦可从网上找到申请开通的方法),这里不详细讲解了,自行百度或者按照微信提示操作,主要讲解接入过程。注册微信,主要的是得到app id和key。下面进入详细接入流程; 1.到微信商户平台...
  • http://www.jianshu.com/p/feb5923c6728 http://www.jianshu.com/p/c97639279d2e
  • 现在微信越来越火了,公众平台也越来越火,作为一个公司或者网站,没有一个公众号,你都不好意思跟人...开发模式需要准备网站并且接入微信后台,在微信目前文档不完善,接口不友好的情况下,本文将详细讲解如何快速接入
  • 微信开发源码讲解

    2014-03-24 13:20:41
    微信开发源码讲解
  • 微信支付服务商接入指引 本文主要针对服务商下特约商户的小程序支付进行讲解。(扫码支付, h5支付大致流程都差不多,了解了小程序支付能够很快接入其他支付类型) 说明:本文中的支付都是指在服务商模式下 支付主体...
  • 原先的网页需要分享到微信,可是转发时,分享的缩略图没有显示,标题和描述都不能自定义现在想让分享显示缩略图。又可以自定义标题和描述,就要用到微信js_sdk。后台基于javaweb,微信的分享流程大致如下,详细见...
  • Python 微信开发

    2020-06-02 23:31:16
    比较好的一部Python之微信开发公开课,属于文字版本,...内容包括:厘清概念、微信开发原理、接入微信公众平台、公众号接收与发送信息、微信网页授权、自定义菜单等,图文并茂的同时,讲解清晰,是一部不错的参考资料。
  • 微信H5开发(二)

    2018-07-15 10:45:14
    第一次使用微信开发文档的时候,感觉文档里面写的内容不算太难,但在实际开发中会碰到很多预想不到的问题。 微信开放平台、微信商务平台 有什么用?都在哪里用? 首先从微信公众号讲起,期间用到微信商务平台和...
1 2 3 4 5 ... 20
收藏数 7,003
精华内容 2,801