目录
- 目录
- ionic2实现微信支付前的准备工作
- ionic2中的微信支付
- ionic2中实现微信支付的参考代码
- 客户端APP
- 实现插件的封装 WechatPlugints
- 引入WechatPlugints文件
- 包装好需要传入的参数WechatPayParam
- 调用微信支付方法sendPaymentRequest
- 服务端PHP实现上述的params参数
- 实际遇到的问题及BUG
- 注意点
- 更多参考
参考:
cordova-plugin-wechat
cordova-plugin-wechat-example的后台PHP文件参考
ionic2实现微信支付前的准备工作
- 理清微信公众号、微信商户平台、微信开放平台三者之间的关系。
- 其中实现PC网页的支付、或者微信客户端中网页的支付,需要在微信公众号,申请支付功能,申请成功后会收到一个微信商户平台的账户。利用该账户可以实现PC网页的扫码支付等。
- 那微信开放平台又是做什么的呢?微信开放平台提供了网页微信登录、APP分享、APP支付等功能。(同时微信开放平台账号需要我们重新申请,不同于微信公众号和微信商户平台)
- 此外,在微信开放平台创建APP应用之后,还需要我们申请支付功能。这个时候,可能大伙就有疑问了。如果我想在PC中实现扫码支付,那么(为了实现这种功能)我已经从微信公众号申请支付功能成功得到了一个微信商户平台账号。然后我又想让APP也实现微信支付,这时候又跑去注册一个微信开放平台的账号,创建好APP应用(当然,准备申请支付功能的时候,它会提示“需要开发者资质认证”(审核费用300元,每年需要重新申请,同时审核失败不会再扣除费用——BOSS跟我说的,它试过几次失败)),又需要申请支付功能。哦我的天,我刚不是已经走了这样的流程了吗?!又有一个微信商户账号来袭,就不能绑定在一起吗?
- 答案:没办法。BOSS打电话给客服后,确定不能绑定在一起,或者说不能使用同一个微信商户账号。
- 可能有人会发现在微信开放平台中有一个“绑定公众号”的功能,这跟上面的问题是木有关系的。
- 微信开放平台中的“绑定公众号”的功能,是为了解决同一公司拥有多个不同公众号导致的同一用户针对不同公众号拥有不同openid的问题,多个公众号绑定在一起后拥有一个相同的unionid的标志。这样就可以实现同一公司不同公众号的同一用户数据共享的问题。
- 好像在微信开放平台中创建的所有应用都共享同一unionid(值)。之前好像在它的文档上看过。不太确定。等有空再翻翻来看看。
- 理解上述问题后再继续我们的ionic2微信支付的入坑之旅吧!!!
ionic2中的微信支付
- 我们需要使用到上述提到的一个cordova插件:cordova-plugin-wechat。
- 根据提示安装该插件:(我嚓,写着写着浏览器奔溃了。写了那么东西说没了就没了。又重从头写起了。可恶的Markdown)
cordova plugin add cordova-plugin-wechat --variable wechatappid=YOUR_WECHAT_APPID
。 - 使用cordova插件需要我们在需要引入的.ts文件中使用declare关键字进行声明(一般放置在 import 语句之下,@Component语句之上),如:
declare var Wechat: any; // 此处声明plugin.xml中clobbers对应的值
。 - 使用Wechat判断客户端是否安装了微信:
Wechat.isInstalled(function (installed) {
alert("Wechat installed: " + (installed ? "Yes" : "No"));
}, function (reason) {
alert("Failed: " + reason);
});
5. 使用Wechat实现ionic2上的微信支付:
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';
注:要根据自己的项目路径引入哦。
包装好需要传入的参数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实现签名
<?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端调起支付的参数列表
返回结果的含义:
名称 | 描述 | 解决方案 |
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”错误后,问题解决。但是由于微信缓存的缘故,导致了调用失败,需要请求微信缓存数据。(我没有尝试过)
- 服务器订单签名:需要保证参数的正确性!!!
更多参考
- Android学习之 移动应用微信支付集成小结
- 微信app支付返回-1错误的情况解决方法
- 微信支付 提示 -1
- 微信APP支付的坑 - errorcode=-1
- 微信支付的坑 返回值 -1
- 微信支付 返回错误代码 -1