Android 快速集成微信支付

2,258 阅读3分钟

写在前面

众所周知,微信的开发者文档写的真的是不敢恭维,各种坑都有,有时候还遇到下载文件地址是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,再做相应的业务逻辑操作(支付结果等就不上图了,还要截图还要上传到服务器,毕竟我是个比较懒的人);

总结

集成微信支付相对简单,就是不能完全照着官方文档来,不然铁定的被坑;