• Android版-微信APP支付


    首发地址: Android版-微信APP支付

    欢迎留言、转发
    微信极速开发系列文章(微信支付、授权获取用户信息等):点击这里

    目录
    1、注册账号、开发者认证
    2、添加应用
    3、申请微信支付
    4、技术开发功能实现步骤介绍
    5、代码实例

    此项目已开源欢迎Start、PR、发起Issues一起讨论交流共同进步
    https://github.com/Javen205/IJPay
    http://git.oschina.net/javen205/IJPay

    微信APP支付接入商户服务中心 官方介绍文档

    1、注册账号、开发者认证

    开放平台直接注册,注册邮箱不能与微信其他的产品同号。

    比较坑的是微信公众号中的支付(微信买单、刷卡、公众号支付、wap支付)以及微信app支付都需要进行微信认证而不是公用一个微信商户平台(需要交两次认证的费用)。

    微信认证这个时间比较短(毕竟交了300大洋)一般一个工作日就会有人联系你核查公司的资料。

    微信认证(开发者资质认证)通过之后就可以在开放平台添加应用了(这个需要审核),应用通过之后就可以申请微信支付了(也需要审核)

    2、添加应用

    这个比较简单,按照提示操作就行 上图

    添加应用-填写基本信息1

    添加应用-填写基本信息2

    添加应用-上传应用图片

    添加应用-填写平台信息

    应用包名只定义,应用签名可以使用资源下载中心的签名生成工具。务必记住包名以及签名keystore文件的密码,如果包名或者签名文件不对打包是唤不起微信支付的。

    资源下载

    下载的资源截图

    应用签名工具

    3、申请微信支付

    如果添加的应用审核通过了(一个工作日),就可以直接申请微信支付了(7个工作日之内)。

    应用审核通过-申请微信支付

    审核通过之后将会收到审核通过的邮件,里面有登录商户平台的登录账户、密码、商户号以及一些操作指引的说明。服务端生成预付订单的签名需要密钥 设置方法可以参考这里

    4、技术开发功能实现

    微信APP支付介绍【文档
    APP端开发步骤说明 【文档

    这里主要聊聊Android微信支付,主要包括以下几个步骤
    1、商户服务端生成订单并在微信平台生成预付订单
    2、客户端调起微信支付进行支付
    3、客户端回调支付结果
    4、服务端接收支付通知

    1、商户服务端生成订单并在微信平台生成预付订单

    调起微信支付前需要服务器生成支付订单再调用【统一下单API】生成预付订单prepayId,再生成签名sign【调起支付API

    以上两个步骤建议都在服务端完成,客户端(Android)通过接口获取对应的参数即可

    2、客户端调起微信支付进行支付

    通过微信提供的jar 唤起微信支付

    调起微信支付

    3、客户端回调支付结果

    参照微信SDK Sample,在net.sourceforge.simcpux.wxapi包路径中实现WXPayEntryActivity类【包名或类名不一致会造成无法回调】
    栗子说明:认真反复读了几遍,感觉这句话有歧义是一个坑,测试的时候一直不回调。这里他想说的意识如下:
    比如你申请应用包名为:javen.com 那么回调的WXPayEntryActivity类必须放到javen.com.wxapi 的包下面

    客户端回调支付结果

    4、服务端接收支付通知

    支付结果通知【官方文档

    代码实现参考开源项目 【点击这里

    5、代码实例

    服务端代码:根据商户订单生成微信预付订单并返回唤起微信支付需要的参数。Demo中参数写成固定了仅供参考

    此项目已开源 【点击这里】 如果对你有帮助请点击Start告诉我 hahaha 。以下代码对应的目录在 com.javen.weixin.controller.WeixinPayController.java

    /**
    	 * 微信APP支付
    	 */
    	public void appPay(){
    		//不用设置授权目录域名
    		//统一下单地址 https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_1#
    		Map<String, String> params = new HashMap<String, String>();
    		params.put("appid", appid);
    		params.put("mch_id", partner);
    		params.put("nonce_str", System.currentTimeMillis() / 1000 + "");
    		params.put("body", "Javen微信公众号极速开发");
    		String out_trade_no=System.currentTimeMillis()+"";
    		params.put("attach", "custom json");
    		params.put("out_trade_no", out_trade_no);
    		int price=10000;
    		params.put("total_fee", price+"");
    		
    		String ip = IpKit.getRealIp(getRequest());
    		if (StrKit.isBlank(ip)) {
    			ip = "127.0.0.1";
    		}
    		
    		params.put("spbill_create_ip", ip);
    		params.put("notify_url", notify_url);
    		params.put("trade_type", "APP");
    
    		String sign = PaymentKit.createSign(params, paternerKey);
    		params.put("sign", sign);
    		
    		String xmlResult = PaymentApi.pushOrder(params);
    		
    System.out.println(xmlResult);
    		Map<String, String> result = PaymentKit.xmlToMap(xmlResult);
    		
    		String return_code = result.get("return_code");
    		String return_msg = result.get("return_msg");
    		if (StrKit.isBlank(return_code) || !"SUCCESS".equals(return_code)) {
    			ajax.addError(return_msg);
    			renderJson(ajax);
    			return;
    		}
    		String result_code = result.get("result_code");
    		if (StrKit.isBlank(result_code) || !"SUCCESS".equals(result_code)) {
    			ajax.addError(return_msg);
    			renderJson(ajax);
    			return;
    		}
    		// 以下字段在return_code 和result_code都为SUCCESS的时候有返回
    		String prepay_id = result.get("prepay_id");
    		//封装调起微信支付的参数 https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_12
    		Map<String, String> packageParams = new HashMap<String, String>();
    		packageParams.put("appid", appid);
    		packageParams.put("partnerid", partner);
    		packageParams.put("prepayid", prepay_id);
    		packageParams.put("package", "Sign=WXPay");
    		packageParams.put("noncestr", System.currentTimeMillis() + "");
    		packageParams.put("timestamp", System.currentTimeMillis() / 1000 + "");
    		String packageSign = PaymentKit.createSign(packageParams, paternerKey);
    		packageParams.put("sign", packageSign);
    		
    		String jsonStr = JsonUtils.toJson(packageParams);
    System.out.println("最新返回apk的参数:"+jsonStr);
    		renderJson(jsonStr);
    	}
    
    客户端代码实现

    使用单例模式统一入口,首先判断微信客户端是否安装,如果有安装再从商户服务器获取调起支付的参数

    public class IPay {
    	private static  IPay mIPay;
    	private Context mContext;
    	
    	private IPay(Context context) {
    		mContext = context;
    	}
    	
    	public static IPay getIntance(Context context){
    		if (mIPay == null) {
    			synchronized(IPay.class){
    				if (mIPay == null) {
    					mIPay = new IPay(context);
    				}
    			}
    		}
    		return mIPay;
    	}
    	
    	//支付结果回调
    	public interface IPayListener{
    		void onPay(int code);
    	}
    	
    	public void toTestPay(Order order,IPayListener listener){
    		if (order != null) {
    			if (IPayLogic.getIntance(mContext.getApplicationContext()).isWeixinAvilible()) {
    				Constants.payListener = listener;
    				new TestPayPrepay(mContext).execute();
    			}else {
    				Toast.makeText(mContext, "未安装微信", Toast.LENGTH_LONG).show();
    			}
    		}else {
    			Toast.makeText(mContext, "参数异常 order is null", Toast.LENGTH_LONG).show();
    		}
    	}
    	
    }
    
    

    调起微信支付、获取调取微信支付参数、判断微信是否安装逻辑实现

    
    public class IPayLogic {
    	private static  IPayLogic mIPayLogic;
    	private Context mContext;
    	
    	private IPayLogic(Context context) {
    		mContext = context;
    	}
    	
    	public static IPayLogic getIntance(Context context){
    		if (mIPayLogic == null) {
    			synchronized(IPayLogic.class){
    				if (mIPayLogic == null) {
    					mIPayLogic = new IPayLogic(context);
    				}
    			}
    		}
    		return mIPayLogic;
    	}
    	
    	//测试
    	public String testPay(){
    		return HttpKit.get(Constants.TESTPAY_URL);
    	}
    	
    	/**
    	 * 调起支付
    	 * @param appId
    	 * @param partnerId
    	 * @param prepayId
    	 * @param nonceStr
    	 * @param timeStamp
    	 * @param sign
    	 */
    	public void startWXPay(String appId,String partnerId,String prepayId,
    			String nonceStr,String timeStamp,String sign){
    
    		IWXAPI api= WXAPIFactory.createWXAPI(mContext, null);
    		api.registerApp(appId);
    		
    		boolean isPaySupported = api.getWXAppSupportAPI() >= Build.PAY_SUPPORTED_SDK_INT;
    		if (!isPaySupported) {
    			Toast.makeText(mContext, "请更新微信客户端", Toast.LENGTH_SHORT).show();
    			return;
    		}
    		
    		PayReq request = new PayReq();
    		request.appId = appId;
    		request.partnerId = partnerId;
    		request.prepayId= prepayId;
    		request.packageValue = "Sign=WXPay";
    		request.nonceStr=nonceStr;
    		request.timeStamp= timeStamp;
    		request.sign= sign;
    		api.sendReq(request);
    	}
    
    	
    	/**
    	 * 判断微信是否安装
    	 * @param context
    	 * @return
    	 */
    	 public  boolean isWeixinAvilible() {
    	        final PackageManager packageManager = mContext.getPackageManager();// 获取packagemanager
    	        List<PackageInfo> pinfo = packageManager.getInstalledPackages(0);// 获取所有已安装程序的包信息
    	        if (pinfo != null) {
    	            for (int i = 0; i < pinfo.size(); i++) {
    	                String pn = pinfo.get(i).packageName;
    	                if (pn.equals("com.tencent.mm")) {
    	                    return true;
    	                }
    	            }
    	        }
    	        return false;
    	    }
    
    }
    
    

    HttpKit MD5 工具类

    
    /**
     * HttpKit
     */
    public class HttpKit {
    	
    	private HttpKit() {}
    	
    	/**
    	 * https 域名校验
    	 */
    	private class TrustAnyHostnameVerifier implements HostnameVerifier {
    		public boolean verify(String hostname, SSLSession session) {
    			return true;
    		}
    	}
    	
    	/**
    	 * https 证书管理
    	 */
    	private class TrustAnyTrustManager implements X509TrustManager {
    		public X509Certificate[] getAcceptedIssuers() {
    			return null;  
    		}
    		public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
    		}
    		public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
    		}
    	}
    	
    	private static final String GET  = "GET";
    	private static final String POST = "POST";
    	private static String CHARSET = "UTF-8";
    	
    	private static final SSLSocketFactory sslSocketFactory = initSSLSocketFactory();
    	private static final TrustAnyHostnameVerifier trustAnyHostnameVerifier = new HttpKit().new TrustAnyHostnameVerifier();
    	
    	private static SSLSocketFactory initSSLSocketFactory() {
    		try {
    			TrustManager[] tm = {new HttpKit().new TrustAnyTrustManager() };
    			SSLContext sslContext = SSLContext.getInstance("TLS");	// ("TLS", "SunJSSE");
    			sslContext.init(null, tm, new java.security.SecureRandom());
    			return sslContext.getSocketFactory();
    		}
    		catch (Exception e) {
    			throw new RuntimeException(e);
    		}
    	}
    	
    	public static void setCharSet(String charSet) {
    		if (charSet.isEmpty()) {
    			throw new IllegalArgumentException("charSet can not be blank.");
    		}
    		HttpKit.CHARSET = charSet;
    	}
    	
    	private static HttpURLConnection getHttpConnection(String url, String method, Map<String, String> headers) throws IOException, NoSuchAlgorithmException, NoSuchProviderException, KeyManagementException {
    		URL _url = new URL(url);
    		HttpURLConnection conn = (HttpURLConnection)_url.openConnection();
    		if (conn instanceof HttpsURLConnection) {
    			((HttpsURLConnection)conn).setSSLSocketFactory(sslSocketFactory);
    			((HttpsURLConnection)conn).setHostnameVerifier(trustAnyHostnameVerifier);
    		}
    		
    		conn.setRequestMethod(method);
    		conn.setDoOutput(true);
    		conn.setDoInput(true);
    		
    		conn.setConnectTimeout(19000);
    		conn.setReadTimeout(19000);
    		
    		conn.setRequestProperty("Content-Type","application/x-www-form-urlencoded");
    		conn.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.146 Safari/537.36");
    		
    		if (headers != null && !headers.isEmpty())
    			for (Entry<String, String> entry : headers.entrySet())
    				conn.setRequestProperty(entry.getKey(), entry.getValue());
    		
    		return conn;
    	}
    	
    	/**
    	 * Send GET request
    	 */
    	public static String get(String url, Map<String, String> queryParas, Map<String, String> headers) {
    		HttpURLConnection conn = null;
    		try {
    			conn = getHttpConnection(buildUrlWithQueryString(url, queryParas), GET, headers);
    			conn.connect();
    			return readResponseString(conn);
    		}
    		catch (Exception e) {
    			throw new RuntimeException(e);
    		}
    		finally {
    			if (conn != null) {
    				conn.disconnect();
    			}
    		}
    	}
    	
    	public static String get(String url, Map<String, String> queryParas) {
    		return get(url, queryParas, null);
    	}
    	
    	public static String get(String url) {
    		return get(url, null, null);
    	}
    	
    	/**
    	 * Send POST request
    	 */
    	public static String post(String url, Map<String, String> queryParas, String data, Map<String, String> headers) {
    		HttpURLConnection conn = null;
    		try {
    			conn = getHttpConnection(buildUrlWithQueryString(url, queryParas), POST, headers);
    			conn.connect();
    			
    			OutputStream out = conn.getOutputStream();
    			out.write(data.getBytes(CHARSET));
    			out.flush();
    			out.close();
    			
    			return readResponseString(conn);
    		}
    		catch (Exception e) {
    			throw new RuntimeException(e);
    		}
    		finally {
    			if (conn != null) {
    				conn.disconnect();
    			}
    		}
    	}
    	
    	public static String post(String url, Map<String, String> queryParas, String data) {
    		return post(url, queryParas, data, null);
    	}
    	
    	public static String post(String url, String data, Map<String, String> headers) {
    		return post(url, null, data, headers);
    	}
    	
    	public static String post(String url, String data) {
    		return post(url, null, data, null);
    	}
    	
    	private static String readResponseString(HttpURLConnection conn) {
    		StringBuilder sb = new StringBuilder();
    		InputStream inputStream = null;
    		try {
    			inputStream = conn.getInputStream();
    			BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, CHARSET));
    			String line = null;
    			while ((line = reader.readLine()) != null){
    				sb.append(line).append("
    ");
    			}
    			return sb.toString();
    		}
    		catch (Exception e) {
    			throw new RuntimeException(e);
    		}
    		finally {
    			if (inputStream != null) {
    				try {
    					inputStream.close();
    				} catch (IOException e) {
    				}
    			}
    		}
    	}
    	
    	/**
    	 * Build queryString of the url
    	 */
    	private static String buildUrlWithQueryString(String url, Map<String, String> queryParas) {
    		if (queryParas == null || queryParas.isEmpty())
    			return url;
    		
    		StringBuilder sb = new StringBuilder(url);
    		boolean isFirst;
    		if (url.indexOf("?") == -1) {
    			isFirst = true;
    			sb.append("?");
    		}
    		else {
    			isFirst = false;
    		}
    		
    		for (Entry<String, String> entry : queryParas.entrySet()) {
    			if (isFirst) isFirst = false;	
    			else sb.append("&");
    			
    			String key = entry.getKey();
    			String value = entry.getValue();
    			if (!value.isEmpty())
    				try {value = URLEncoder.encode(value, CHARSET);} catch (UnsupportedEncodingException e) {throw new RuntimeException(e);}
    			sb.append(key).append("=").append(value);
    		}
    		return sb.toString();
    	}
    }
    
    
    public class MD5 {
        public static String MD5sign(String s) {
            char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
            try {
                byte[] btInput = s.getBytes("UTF-8");
                // 获得MD5摘要算法的 MessageDigest 对象
                MessageDigest mdInst = MessageDigest.getInstance("MD5");
                // 使用指定的字节更新摘要
                mdInst.update(btInput);
                // 获得密文
                byte[] md = mdInst.digest();
                // 把密文转换成十六进制的字符串形式
                int j = md.length;
                char str[] = new char[j * 2];
                int k = 0;
                for (int i = 0; i < j; i++) {
                    byte byte0 = md[i];
                    str[k++] = hexDigits[byte0 >>> 4 & 0xf];
                    str[k++] = hexDigits[byte0 & 0xf];
                }
                return new String(str).toLowerCase();
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        }
    
    
    	public static void main(String[] args) {
    		System.out.println(MD5sign("Hello world"));
    	}
    }
    
    

    使用AsyncTask异步获取调起微信支付的相关参数。当然你也可以使用其他的异步网络请求开源框架

    
    public class TestPayPrepay extends AsyncTask<Object, Integer, String> {
    	private Context mContext;
    	public TestPayPrepay(Context context) {
    		this.mContext = context;
    	}
    	
    	@Override
    	protected String doInBackground(Object... params) {
    		System.out.println("TestPayPrepay doInBackground");
    		return  IPayLogic.getIntance(mContext).testPay();
    	}
    	
    	@Override
    	protected void onPostExecute(String result) {
    		try {
    			if (result!=null) {
    				System.out.println("TestPayPrepay result>"+result);
    				JSONObject data = new JSONObject(result);
    				if(!data.has("code")){
    					String sign = data.getString("sign");
    					String timestamp = data.getString("timestamp");
    					String noncestr = data.getString("noncestr");
    					String partnerid = data.getString("partnerid");
    					String prepayid = data.getString("prepayid");
    					String appid = data.getString("appid");
    					Toast.makeText(mContext, "正在调起支付", Toast.LENGTH_SHORT).show();
    					
    					Constants.APP_ID = appid;
    					
    					IPayLogic.getIntance(mContext).startWXPay(appid, partnerid, prepayid, noncestr, timestamp, sign);
    				}else{
    					String message = data.getString("message");
    		        	Log.d("PAY_GET", "返回错误"+message);
    		        	Toast.makeText(mContext, "返回错误:"+message, Toast.LENGTH_SHORT).show();
    				}
    			}else {
    				System.out.println("get  prepayid exception, is null");
    			}
    		} catch (Exception e) {
    			Log.e("PAY_GET", "异常:"+e.getMessage());
            	Toast.makeText(mContext, "异常:"+e.getMessage(), Toast.LENGTH_SHORT).show();
            }
    		super.onPostExecute(result);
    	}
    
    }
    
    

    支付结果回调

     <activity
                android:name="[应用的包名].wxapi.WXPayEntryActivity"
                android:exported="true"
                android:theme="@android:style/Theme.Translucent"
                android:launchMode="singleTop" >
     </activity>
    

    封装的是SDK 所以这里设置了一个透明的主题

    
    public class WXPayEntryActivity extends Activity implements IWXAPIEventHandler{
        private IWXAPI api;
    	private IPayListener payListener;
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            
            requestWindowFeature(Window.FEATURE_NO_TITLE);//隐藏标题
            LinearLayout ll = new LinearLayout(this);
            setContentView(ll);
        	api = WXAPIFactory.createWXAPI(this, Constants.APP_ID);
            api.handleIntent(getIntent(), this);
            finish();
        }
    
    	@Override
    	protected void onNewIntent(Intent intent) {
    		super.onNewIntent(intent);
    		setIntent(intent);
            api.handleIntent(intent, this);
    	}
    
    	@Override
    	public void onReq(BaseReq req) {
    	}
    
    	@Override
    	public void onResp(BaseResp resp) {
    		payListener = Constants.payListener;
    		int code = resp.errCode;  
    		System.out.println("onResp errCode>"+code);
    		if (payListener!=null) {
    			payListener.onPay(code);
    			System.out.println("payListener callback");
    		}else {
    			System.out.println("payListener not callback");
    		}
    	}
    }
    

    注意:如果回调的code一直返回-1
    1、请检查应用包名以及apk 的签名是否与你提交到微信开放平台的一致
    2、请检查返回调取微信支付的参数是否正确
    大部分原因是第一种

    微信APP支付.png

    遗留问题:由于支付应用的包名不固定WXPayEntryActivity无法封装到jar中,需要单独在支付应用添加.wxapi 这个包名并复制 WXPayEntryActivity到此包中。如果有好的解决方案欢迎留言

    微信开发系列文章 http://www.jianshu.com/p/a172a1b69fdd

    推荐阅读
    极速开发微信公众号之微信买单
    极速开发微信公众号之公众号支付
    极速开发微信公众号之扫码支付
    极速开发微信公众号之刷卡支付
    极速开发微信公众号之现金红包
    极速开发微信公众号之模板消息

    如果此文章对你有帮助请点击喜欢告诉我

    服务端源码地址:http://git.oschina.net/javen205/weixin_guide
    客户端源码地址:https://github.com/Javen205/HelloAndroid

  • 相关阅读:
    【HDU1724】Ellipse-自适应Simpson积分法
    【HDU1724】Ellipse-自适应Simpson积分法
    【51Nod1227】平均最小公倍数-杜教筛
    【51Nod1227】平均最小公倍数-杜教筛
    【HDU5628】Clarke and math-狄利克雷卷积+快速幂
    【HDU5628】Clarke and math-狄利克雷卷积+快速幂
    deleted
    deleted
    deleted
    deleted
  • 原文地址:https://www.cnblogs.com/zyw-205520/p/6107330.html
Copyright © 2020-2023  润新知