写在前面
众所周知,微信的开发者文档写的真的是不敢恭维,各种坑都有,有时候还遇到下载文件地址是localhost的状态,表示…,吐槽完了,对接工作还是要做的,下面开始对接微信移动支付功能;
准备工作
账号申请,签约移动支付这些就不说了;
1、APPID
2、商户号
3、商户key(在商户平台设置的API密钥)
准备好这三样东西就可以动手撸码了;
我们先来梳理下从生成预支付单到调起支付的整体流程:
1、app请求后台服务器生成预支付订单;
2、后台服务器调用微信统一下单接口生成预支付订单;
3、后台服务器拿到微信统一下单接口返回的prepayid后重新签名返回给app;
4、app拿到后台服务器返回的签名数据后调起微信支付;
这里有个建议,在调用微信接口的时候有一些不是必填的参数能不填就不填,以免给自己带来不必要的坑;
资源下载
服务端开发
这里我用的是PHP,java苦逼在没有tomcat服务器就用PHP简单实现;
先贴出PHP的全部代码再分析流程,注释清晰;
array(
'method' => 'POST',
'content' => $data
));
if ($optional_headers !== null) {
$params['http']['header'] = $optional_headers;
}
$ctx = stream_context_create($params);
$fp = @fopen($url, 'rb', false, $ctx);
if (!$fp) {
throw new Exception("Problem with $url, $php_errormsg");
}
$response = @stream_get_contents($fp);
if ($response === false) {
throw new Exception("Problem reading data from $url, $php_errormsg");
}
return $response;
}
/**
* 生成随机数
*/
function getRandom($param){
$str="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
$key = "";
for($i=0;$i<$param;$i++)
{
$key .= $str{mt_rand(0,32)}; //生成php随机数
}
return $key;
}
/**
* 生成签名
*/
function getSign(){
global $appid;
global $attach;
global $body;
global $mch_id;
global $nonce_str;
global $notify_url;
global $out_trade_no;
global $spbill_create_ip;
global $total_fee;
global $trade_type;
$str = "appid=".$appid."&attach=".$attach."&body=".$body."&mch_id=".$mch_id."&nonce_str=".$nonce_str."¬ify_url=".$notify_url."&out_trade_no=".
$out_trade_no."&spbill_create_ip=".$spbill_create_ip."&total_fee=".$total_fee."&trade_type=".$trade_type."&key=商户key";
$strMD5 = md5($str);
return strtoupper($strMD5);
}
/**
* 再次生成签名返回给app
*/
function getSign2($prepayid){
global $appid;
global $timestamp;
global $noncestr;
$package = "Sign=WXPay";
$partnerid = "1278337201";
$str = "appid=".$appid."&noncestr=".$noncestr."&package=".$package."&partnerid=".$partnerid."&prepayid=".$prepayid."×tamp=".$timestamp."&key=商户key";
$strMD5 = md5($str);
return strtoupper($strMD5);
}
function getObj($status,$prepay_id,$nonce_str,$timestamp,$sign){
if ($status!=200) {
$obj = '{
"status": 201,
"message": "失败",
"params": {
}
}';
return $obj;
}else{
global $appid;
global $mch_id;
$obj = '{
"status": 200,
"message": "成功",
"params": {
"prepay_id": "'.$prepay_id.'",
"nonce_str": "'.$nonce_str.'",
"timestamp": "'.$timestamp.'",
"sign": "'.$sign.'",
"appId": "'.$appid.'",
"partnerId":"'.$mch_id.'",
"packageValue":"Sign=WXPay"
}
}';
return $obj;
}
}
$post_data="
".$appid."
".$attach."
".$body."
".$mch_id."
".$nonce_str."
".$notify_url."
".$out_trade_no."
".$spbill_create_ip."
".$total_fee."
".$trade_type."
".getSign()."
";
$url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
$xmlstring = do_post_request($url,$post_data,NULL);
$doc=new DOMDocument();
$doc->loadXML($xmlstring);
$return_code = $doc->getElementsByTagname('return_code')->item(0)->nodeValue;
if($return_code=="SUCCESS"){
//成功
$prepay_id = $doc->getElementsByTagname('prepay_id')->item(0)->nodeValue;
$sign = getSign2($prepay_id);
echo getObj(200,$prepay_id,$nonce_str,$timestamp,$sign);
}else{
//调起统一下单接口失败
echo getObj(201,null,null,null,null);
}
?>
注意:
1、两次MD5的参数都必须按照字典序进行排序;
2、生成时间戳要使用精度为秒的(10位),不能使用精度为毫秒的(13位),PHP的time()本身就是10位的,老天庇佑,让我没掉坑里;
3、再次签名除了prepay_id用微信返回的,其他参与签名的参数全用第一次自己生成的数据;
4、再次签名字段命名千万不能跟官方文档一样,老老实实全部写成小写
APP开发
1、引入微信的库
2、在包名下新建wxapi包,创建WXPayEntryActivity类,例:
注意:包名和类名必须为wxapi和WXPayEntryActivity
public class WXPayEntryActivity extends AppCompatActivity implements IWXAPIEventHandler {
private IWXAPI api;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
api = WXAPIFactory.createWXAPI(this, PayConfig.WX_APP_ID);
api.handleIntent(getIntent(), this);
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
api.handleIntent(intent, this);
}
@Override
public void onReq(BaseReq baseReq) {
}
/**
* 回调结果的处理
*
* @param baseReq
*/
@Override
public void onResp(BaseResp baseResp) {
if (baseResp.getType() == ConstantsAPI.COMMAND_PAY_BY_WX) {
if (baseResp.errCode == 0) {
Toast.makeText(this, "支付成功", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "支付失败" + baseResp.errCode + "str:" + baseResp.errStr, Toast.LENGTH_SHORT).show();
}
Log.i("------", "支付失败" + baseResp.errCode + "str:" + baseResp.transaction);
finish();
}
}
}
3、配置AndroidManifest文件,例:
4、在需要调用微信支付的地方调用服务器,获取返回的数据后调起微信支付,例:
OrderBean是我自定义的实体类对象,也就是后台服务器返回的数据
public void sendReq(OrderBean mOrderBean) {
IWXAPI api = WXAPIFactory.createWXAPI(mContext, PayConfig.WX_APP_ID);
api.registerApp(PayConfig.WX_APP_ID);
PayReq payRequest = new PayReq();
payRequest.appId = mOrderBean.getAppId();
payRequest.partnerId = mOrderBean.getPartnerId();
payRequest.prepayId = mOrderBean.getPrepay_id();
payRequest.packageValue = mOrderBean.getPackageValue();
payRequest.nonceStr = mOrderBean.getNonce_str();
payRequest.timeStamp = mOrderBean.getTimestamp();
payRequest.sign = mOrderBean.getSign();
boolean x = api.sendReq(payRequest);
if (!x) {
Toast.makeText(mContext, "请先安装微信客户端", Toast.LENGTH_SHORT).show();
}
}
5、支付结果会回调到步骤2中的onResp,再做相应的业务逻辑操作(支付结果等就不上图了,还要截图还要上传到服务器,毕竟我是个比较懒的人);
总结
集成微信支付相对简单,就是不能完全照着官方文档来,不然铁定的被坑;