• 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

  • 相关阅读:
    Poj 2017 Speed Limit(水题)
    Poj 1316 Self Numbers(水题)
    Poj 1017 Packets(贪心策略)
    Poj 1017 Packets(贪心策略)
    Poj 2662,2909 Goldbach's Conjecture (素数判定)
    Poj 2662,2909 Goldbach's Conjecture (素数判定)
    poj 2388 Who's in the Middle(快速排序求中位数)
    poj 2388 Who's in the Middle(快速排序求中位数)
    poj 2000 Gold Coins(水题)
    poj 2000 Gold Coins(水题)
  • 原文地址:https://www.cnblogs.com/zyw-205520/p/6107330.html
Copyright © 2020-2023  润新知