这回又来个 ionic2 的微信支付!(遇到一些莫名的BUG)

目录

参考:
cordova-plugin-wechat
cordova-plugin-wechat-example的后台PHP文件参考javascript

ionic2实现微信支付前的准备工做

  1. 理清微信公众号、微信商户平台、微信开放平台三者之间的关系。
  2. 其中实现PC网页的支付、或者微信客户端中网页的支付,须要在微信公众号,申请支付功能,申请成功后会收到一个微信商户平台的帐户。利用该帐户能够实现PC网页的扫码支付等。
  3. 那微信开放平台又是作什么的呢?微信开放平台提供了网页微信登陆、APP分享、APP支付等功能。(同时微信开放平台帐号须要咱们从新申请,不一样于微信公众号和微信商户平台)
  4. 此外,在微信开放平台建立APP应用以后,还须要咱们申请支付功能。这个时候,可能大伙就有疑问了。若是我想在PC中实现扫码支付,那么(为了实现这种功能)我已经从微信公众号申请支付功能成功获得了一个微信商户平台帐号。而后我又想让APP也实现微信支付,这时候又跑去注册一个微信开放平台的帐号,建立好APP应用(固然,准备申请支付功能的时候,它会提示“须要开发者资质认证”(审核费用300元,每一年须要从新申请,同时审核失败不会再扣除费用——BOSS跟我说的,它试过几回失败)),又须要申请支付功能。哦个人天,我刚不是已经走了这样的流程了吗?!又有一个微信商户帐号来袭,就不能绑定在一块儿吗?
  5. 答案:没办法。BOSS打电话给客服后,肯定不能绑定在一块儿,或者说不能使用同一个微信商户帐号。
  6. 可能有人会发如今微信开放平台中有一个“绑定公众号”的功能,这跟上面的问题是木有关系的。
  7. 微信开放平台中的“绑定公众号”的功能,是为了解决同一公司拥有多个不一样公众号致使的同一用户针对不一样公众号拥有不一样openid的问题,多个公众号绑定在一块儿后拥有一个相同的unionid的标志。这样就能够实现同一公司不一样公众号的同一用户数据共享的问题。
  8. 好像在微信开放平台中建立的全部应用都共享同一unionid(值)。以前好像在它的文档上看过。不太肯定。等有空再翻翻来看看。
  9. 理解上述问题后再继续咱们的ionic2微信支付的入坑之旅吧!!!

ionic2中的微信支付

  1. 咱们须要使用到上述提到的一个cordova插件:cordova-plugin-wechat。
  2. 根据提示安装该插件:(我嚓,写着写着浏览器奔溃了。写了那么东西说没了就没了。又重从头写起了。可恶的Markdown)cordova plugin add cordova-plugin-wechat --variable wechatappid=YOUR_WECHAT_APPID
  3. 使用cordova插件须要咱们在须要引入的.ts文件中使用declare关键字进行声明(通常放置在 import 语句之下,@Component语句之上),如:declare var Wechat: any; // 此处声明plugin.xml中clobbers对应的值
  4. 使用Wechat判断客户端是否安装了微信:
Wechat.isInstalled(function (installed) {
    alert("Wechat installed: " + (installed ? "Yes" : "No"));
}, function (reason) {
    alert("Failed: " + reason);
});

  5. 使用Wechat实现ionic2上的微信支付:php

Wechat.share({
    message: {
        title: "Hi, there",
        description: "This is description.",
        thumb: "www/img/thumbnail.png",
        mediaTagName: "TEST-TAG-001",
        messageExt: "这是第三方带的测试字段",
        messageAction: "<action>dotalist</action>",
        media: "YOUR_MEDIA_OBJECT_HERE"
    },
    scene: Wechat.Scene.TIMELINE   // share to Timeline
}, function () {
    alert("Success");
}, function (reason) {
    alert("Failed: " + reason);
});

ionic2中实现微信支付的参考代码

客户端(APP)

实现插件的封装 WechatPlugin.ts

declare var Wechat: any;  // 此处声明plugin.xml中clobbers对应的值 

export interface WechatPayParam {  
  partnerid: string;  
  prepayid: string;  
  noncestr: string;  
  timestamp: string;  
  sign: string;  
}  

export class WechatPlugin {  

  public static isInstalled() {  
    return new Promise((resolve, reject) => {  
      Wechat.isInstalled(result => {  
        resolve(result);  
      }, error => {  
        reject(error);  
      });  
    });  
  }  

  public static sendPaymentRequest(params: WechatPayParam) {  
    return new Promise((resolve, reject) => {  
      Wechat.sendPaymentRequest(params, result => {  
        resolve(result);  
      }, error => {  
        reject(error);  
      });  
    });  
  }  
}

引入WechatPlugin.ts文件

import {WechatPlugin} from 'wechat-plugin';

注:要根据本身的项目路径引入哦。html

包装好须要传入的参数WechatPayParam

var params = {
    partnerid: this.payResult.partnerid, // merchant id
    prepayid: this.payResult.prepayid, // prepay id
    noncestr: this.payResult.noncestr, // nonce
    timestamp: this.payResult.timestamp, // timestamp
    sign: this.payResult.sign // signed string
};

调用微信支付方法sendPaymentRequest

WechatPlugin.sendPaymentRequest(params).then((result) => { // xxx }), (error) => {
        // xxx
    });

服务端(PHP实现上述的params参数)

注:听parnter说他在github上找了个。因为当前木有copy到他的,这里就先不贴。
demo.php
参考:微信支付PHP实现签名java

<?php 
// APPID (开户邮件中可查看)
define("APP_ID",  "YOUR_APP_ID");
// 商户号 (开户邮件中可查看)
define("MCH_ID",  "YOUR_MCH_ID");
// 商户支付密钥 (https://pay.weixin.qq.com/index.php/account/api_cert)
define("APP_KEY", "YOUR_APP_KEY");
// get prepay id
$prepay_id = generatePrepayId(APP_ID, MCH_ID);
// re-sign it
$response = array(
    'appid'     => APP_ID,
    'partnerid' => MCH_ID,
    'prepayid'  => $prepay_id,
    'package'   => 'Sign=WXPay',
    'noncestr'  => generateNonce(),
    'timestamp' => time(),
);
$response['sign'] = calculateSign($response, APP_KEY);
// send it to APP
echo json_encode($response);
/** * Generate a nonce string * * @link https://pay.weixin.qq.com/wiki/doc/api/app.php?chapter=4_3 */
function generateNonce() {
    return md5(uniqid('', true));
}
/** * Get a sign string from array using app key * * @link https://pay.weixin.qq.com/wiki/doc/api/app.php?chapter=4_3 */
function calculateSign($arr, $key) {
    ksort($arr);
    $buff = "";
    foreach ($arr as $k => $v) {
        if ($k != "sign" && $k != "key" && $v != "" && !is_array($v)){
            $buff .= $k . "=" . $v . "&";
        }
    }
    $buff = trim($buff, "&");
    return strtoupper(md5($buff . "&key=" . $key));
}
/** * Get xml from array */
function getXMLFromArray($arr) {
    $xml = "<xml>";
    foreach ($arr as $key => $val) {
        if (is_numeric($val)) {
            $xml .= sprintf("<%s>%s</%s>", $key, $val, $key);
        } else {
            $xml .= sprintf("<%s><![CDATA[%s]]></%s>", $key, $val, $key);
        }
    }
    $xml .= "</xml>";
    return $xml;
}
/** * Generate a prepay id * * @link https://pay.weixin.qq.com/wiki/doc/api/app.php?chapter=9_1 */
function generatePrepayId($app_id, $mch_id) {
    $params = array(
        'appid'            => $app_id,
        'mch_id'           => $mch_id,
        'nonce_str'        => generateNonce(),
        'body'             => 'Test product name',
        'out_trade_no'     => time(),
        'total_fee'        => 1,
        'spbill_create_ip' => '8.8.8.8',
        'notify_url'       => 'http://localhost',
        'trade_type'       => 'APP',
    );
    // add sign
    $params['sign'] = calculateSign($params, APP_KEY);
    // create xml
    $xml = getXMLFromArray($params);
    // send request
    $ch = curl_init();
    curl_setopt_array($ch, array(
        CURLOPT_URL            => "https://api.mch.weixin.qq.com/pay/unifiedorder",
        CURLOPT_POST           => true,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HTTPHEADER     => array('Content-Type: text/xml'),
        CURLOPT_POSTFIELDS     => $xml,
    ));
    $result = curl_exec($ch);
    curl_close($ch);
    // get the prepay id from response
    $xml = simplexml_load_string($result);
    return (string)$xml->prepay_id;
}

实际遇到的问题及BUG

  最最直观的BUG就属:明明服务端返回的params中的参数都正确了可是却一直不能调起微信支付,而且提示“Failed:普通错误”。
  注:以致于我瞎搞了好久,试了不少解决方案。最后莫名地“试”成功了。下面说说几种可能致使错误的事咯。
  实际上应该是对应微信支付中错误码“-1”。参考:APP端调起支付的参数列表
  返回结果的含义:python

名称 描述 解决方案
0 成功 展现成功页面
-1 错误 可能的缘由:签名错误、未注册APPID、项目设置APPID不正确、注册的APPID与设置的不匹配、其余异常等。
-2 用户取消 无需处理。发生场景:用户不支付了,点击取消,返回APP。

注意点

  • APP打包进行测试时,须要使用keystore给apk加入签名。(能够参考:我另外一篇文章:ionic发布App前的工做,别觉得build一下就完事了(Publishing your app)
  • 同时在微信开放平台中对应的APP应用中给Android设置应用签名,即便用keystore给apk加入的签名。须要保持该应用签名同安装包一致(固然在微信开放平台中设置的包名与须要同应用中的一致)。apk签名的获取可使用微信提供的一个APP应用,输入包名进行获取。参考连接:获取安装到手机的第三方应用签名的apk包
  • timestamp参数为数字类型,而不是字符串。(猜想。至少我改为数字类型后成功了。具体没去修改为字符串再测试)
  • 程序报“-1”错误后,问题解决。可是因为微信缓存的缘故,致使了调用失败,须要请求微信缓存数据。(我没有尝试过)
  • 服务器订单签名:须要保证参数的正确性!!!

更多参考