根据微信官网的介绍,微信支付共有6种方式,本文介绍是APP支付。APP支付又称移动端支付,是商户通过在移动端应用APP中集成开放SDK调起微信支付模块完成支付的模式。

支付流程

1、用户在APP内选中商品或服务,点击支付。

2、APP将支付订单信息发送给APP后台进行处理。

3、APP后台将签名后的订单信息返回。

4、APP内调用微信支付接口发送支付请求。

5、请求成功,则唤起微信支付界面;如果失败,则在回调函数内进行错误信息处理和提示。

6、微信支付系统将支付结果发送APP后台

7、APP查询后台,获取支付结果。

下面是官网给出的详细流程图:

v3版android调用微信支付 android 微信支付_json

运行官网APP Demo

下载官网示例,压缩包是SDKSample_Android_v3_pay.zip。由于版本的问题,官网给的deomo并不能直接运行,需要很多修改。跑起来界面如下,本文只研究了微信支付。

v3版android调用微信支付 android 微信支付_v3版android调用微信支付_02


1、解压后发现是Eclipse工程,将其导入到AndroidStudio中,这时会默认复制一份新文件夹。需要手动将debug.keystore文件拷贝到新项目文件夹中。

2、导入后修改两个build.gradle如下:

buildscript {
    repositories {
        google()
        jcenter()
        maven { url 'http://maven.aliyun.com/nexus/content/groups/public/' }
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.2.1'//替换你自己的as版本号
    }
}

allprojects {
    repositories {
        google()
        jcenter()
        maven { url 'http://maven.aliyun.com/nexus/content/groups/public/' }
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}
apply plugin: 'com.android.application'

android {
    compileSdkVersion 28

    defaultConfig {
        applicationId "net.sourceforge.simcpux"
        minSdkVersion 16
        targetSdkVersion 28
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
        }
    }

    signingConfigs {
        debug {
            storeFile file("../debug.keystore")
        }
    }
}

repositories {
    flatDir {
        dirs 'libs' //this way we can find the .aar file in libs folder
    }
}

dependencies {
    api 'com.tencent.mm.opensdk:wechat-sdk-android-with-mta:5.3.1'//新版本的api使用方式,不需要再手动引入libammsdk.jar和wechat-sdk-android-with-mta-1.0.2.jar两个文件
    api 'com.android.support:appcompat-v7:28.0.0'
    implementation 'com.android.support:appcompat-v7:28.0.0'
    implementation 'junit:junit:4.12'
}
android {
    useLibrary 'org.apache.http.legacy'
}

3、将所有命名空间com.tencent.mm.sdk改为com.tencent.mm.opensdk 4、编译一下。如果出现下面提示:

failed linking file resources
generated id ‘android:id/*******’ for external package ‘android’.

有两个办法:
1)定位到错误文件位置:将android:id="@+android:id/title"改为android:id="@android:id/title" 2)忽略这些错误。因为如果没有其他错误,这些错误最后也会消失(是不是有点绕?)
5、WXImageObject的属性imgUrl替换为imagePath(这个错误跟微信支付无关,根据错误提示修改就好了)
6、还有最关键一步要改.demo里的支付代码主要在PayActivity的appayBtn里,这里模拟请求了一个后台订单信息,用到了HttpClient类,但是as开发要求所有耗时操作必须放到异步里执行。因此需要对原代码进行改造:

@Override
			public void onClick(View v) {
				String url = "https://wxpay.wxutil.com/pub_v2/app/app_pay.php";
				Button payBtn = (Button) findViewById(R.id.appay_btn);
				payBtn.setEnabled(false);
				Toast.makeText(PayActivity.this, "获取订单中...", Toast.LENGTH_SHORT).show();
				@SuppressLint("StaticFieldLeak") AsyncTask<String, Integer, Void> atask = new AsyncTask<String, Integer, Void>() {
					@Override
					protected Void doInBackground(String... strings) {
						//将请求操作放到异步中执行
						try{
							byte[] buf = Util.httpGet(strings[0]);
							if (buf != null && buf.length > 0) {
								String content = new String(buf);
								Log.e("get server pay params:",content);
								JSONObject json = new JSONObject(content);
								if(null != json && !json.has("retcode") ){
									PayReq req = new PayReq();
									//req.appId = "wxf8b4f85f3a794e77";  // 测试用appId
									req.appId			= json.getString("appid");
									req.partnerId		= json.getString("partnerid");
									req.prepayId		= json.getString("prepayid");
									req.nonceStr		= json.getString("noncestr");
									req.timeStamp		= json.getString("timestamp");
									req.packageValue	= json.getString("package");
									req.sign			= json.getString("sign");
									req.extData			= "app data"; // optional
									//Toast.makeText(PayActivity.this, "正常调起支付", Toast.LENGTH_SHORT).show();
									System.out.println("正常调起支付...");
									System.out.println("检查支付参数checkArgs:" +req.checkArgs() );
									// 在支付之前,如果应用没有注册到微信,应该先调用IWXMsg.registerApp将应用注册到微信
									api.sendReq(req);
								}else{
									Log.d("PAY_GET", "返回错误"+json.getString("retmsg"));
									//Toast.makeText(PayActivity.this, "返回错误"+json.getString("retmsg"), Toast.LENGTH_SHORT).show();
								}
							}else{
								Log.d("PAY_GET", "服务器请求错误");
								//Toast.makeText(PayActivity.this, "服务器请求错误", Toast.LENGTH_SHORT).show();
							}
						}catch(Exception e){
							Log.e("PAY_GET", "异常:"+e.getMessage());
							Toast.makeText(PayActivity.this, "异常:"+e.getMessage(), Toast.LENGTH_SHORT).show();
						}
						return  null;
					}
				};
				atask.execute(url);
		        payBtn.setEnabled(true);
			}

7、运行后,点击支付,返回结果如下。(注意:这里返回结果-1,是因为appId没有正确设置。但是如果是在一部手机上第一次运行demo且第一次点击微信支付,是能成功一次的,再点击就一直返回 -1 了)。

ps:正在申请自己的appid,然后跑自己的demo试试。

v3版android调用微信支付 android 微信支付_androidstudio_03

app后台都干了啥

APP后台主要做了两件事:

  • 从微信支付系统那获取预付单信息。
  • 接收并保存支付结果。

首先看第一件事:
1、APP后台生成支付订单。
2、调用统一下单API接口详请) 。这里有两个安全控制参数:随机字符串nonce_str和签名sign。nonce_str是由随机数算法生成的不长于32位的随机字符串,用于防范重放攻击;sign是由签名生成算法生成的签名信息,防止信息窃取篡改。
3、获取生成的预付单。如果下单API调用成功,则能获取到prepay_id。这是微信生成的预支付回话标识,用于后续接口调用中使用,该值有效期为2小时
4、将参数再次签名传输给APP发起支付。注意:这里sign的签名方式一定要与统一下单接口使用的一致。

再看第二件事:
支付完成后,App 后台需要接收处理支付通知。注意:
1、要做签名验证,并校验返回的订单金额是否与商户侧的订单金额一致,防止数据泄漏导致出现“假通知”,造成资金损失),并按文档规范返回应答。
2、当收到通知进行处理时,首先检查对应业务数据的状态,判断该通知是否已经处理过,如果没有处理过再进行处理,如果处理过直接返回结果成功。在对业务数据进行状态检查和处理之前,要采用数据锁进行并发控制,以避免函数重入造成的数据混乱。

微信提供的其它API

  • 查询订单:该接口提供所有微信支付订单的查询,商户可以通过该接口主动查询订单状态,完成下一步的业务逻辑。
  • 关闭订单:以下情况需要调用关单接口:商户订单支付失败需要生成新单号重新发起支付,要对原订单号调用关单,避免重复支付;系统下单后,用户支付超时,系统退出不再受理,避免用户继续,请调用关单接口。
  • 申请退款 :当交易发生之后一段时间内,由于买家或者卖家的原因需要退款时,卖家可以通过退款接口将支付款退还给买家,微信支付将在收到退款请求并且验证成功之后,按照退款规则将支付款按原路退到买家帐号上。
  • 查询退款:提交退款申请后,通过调用该接口查询退款状态。退款有一定延时,用零钱支付的退款20分钟内到账,银行卡支付的退款3个工作日后重新查询退款状态。
  • 下载对账单 :商户可以通过该接口下载历史交易清单。比如掉单、系统错误等导致商户侧和微信侧数据不一致,通过对账单核对后可校正支付状态。
  • 下载资金账单 :商户可以通过该接口下载自2017年6月1日起 的历史资金流水账单。
  • 退款结果通知:当商户申请的退款有结果后,微信会把相关结果发送给商户,商户需要接收处理,并返回应答。