使用 EasyPay 打造全能移动支付框架

qvpk5657 8年前
   <h2>前言</h2>    <p>在这之前,笔者发布了两篇移动app支付相关博文,得到一些关注,但是由于博文中代码零碎,有些读者私信博主,以及加笔者qq咨询相关问题。考虑到这些,笔者把之前项目中的支付相关代码从业务中剥离出来,重构,形成了现在的 EasyPay 。</p>    <p>EasyPay项目地址: <a href="/misc/goto?guid=4959745927359024803" rel="nofollow,noindex">https://github.com/xiong-it/EasyPay</a></p>    <p>目前项目还没有readme,这个文章暂且当做readme吧。</p>    <h2>EasyPay是什么</h2>    <p>EasyPay旨在帮助Android开发者快速集成接入移动支付SDK,其中包括主流的 <strong>微信APP支付</strong> , <strong>支付宝APP支付</strong> ,银联支付(开发中)。</p>    <h2>为什么要用EasyPay</h2>    <h2>EasyPay和微信支付等移动支付SDK的区别</h2>    <p>EasyPay是一个开源的聚合支付可定制化框架,目前已集成微信APP支付,支付宝APP支付SDK。银联支付(开发中)。</p>    <p>Android开发者只需要简单调用EasyPay的几行代码,即可调起支付客户端,完成支付流程,得到支付结果。</p>    <p>EasyPay宗旨:简单,易用,可扩展。</p>    <h2>EasyPay和其他第三方聚合支付的区别</h2>    <p>第三方聚合支付,如知名的Ping++,需要同时接入其Server端SDK和Client端SDK,使接入企业面临风险:</p>    <p>1、信息泄露风险</p>    <p>2、支付集成服务商提供服务跟不上商户业务发展需要的风险</p>    <p>3、支付集成服务商系统稳定性、安全性的风险</p>    <p>4、资金安全风险</p>    <p>此外,天下没有免费的午餐,第三方聚合支付平台一般需要收取 <strong>5%~15%</strong> 左右的手续费等各种服务费用,使得接入企业收益受损。</p>    <p>而使用开源的 EasyPay ,代码透明,与Server端无关,Android开发者只需要根据自己需求对EasyPay进行个性化定制,即可打造一个支付平台齐全的无风险支付框架。但是客观的讲,这同时也是EasyPay的短板,它只简化了APP端开发者的调用工作,Server端工作人员仍需要按照移动支付第三方平台的SDK文档进行开发。</p>    <p>如果觉得 EasyPay 对你有帮助,你付出的仅仅是一个点赞,或者一个star或者fork,如果不满意,请帮忙提issue指出,而不是5%-15%左右的手续费等各种服务费用。</p>    <p>通过阅读EasyPay源码,你可以知道移动支付的流程是怎样的:</p>    <pre>  <code class="language-java">APP->APP服务器->支付平台后台服务器->APP服务器->APP->支付客户端->APP</code></pre>    <p>通过扩展EasyPay,你可以较快的搭建一个私有的功能完善的支付框架。</p>    <h2>EasyPay怎么用</h2>    <p>用户场景:</p>    <p>APP用户选择一个价格为666元的商品:"皮皮虾",商品描述:"此商品属性过于强大,难以调教,一般人切勿轻易购买,吼吼!",然后用户进入收款台,选择了微信支付。</p>    <p>好勒,皮皮虾,咱们走!此处省略:皮皮虾,咱们走.jpg</p>    <pre>  <code class="language-java">PayParams params = new PayParams.Builder(this)                  .wechatAppID("your_wechat_appid")// 仅当支付方式选择微信支付时需要此参数                  .payWay(PayWay.WechatPay)                  .goodsPrice(66600)// 单位为:分                  .goodsName("皮皮虾")                  .goodsIntroduction("此商品属性过于强大,难以调教,一般人切勿轻易购买,吼吼!")                  .httpType(HttpType.Get)                  .httpClientType(NetworkClientType.Retrofit)                  .requestBaseUrl("http://blog.csdn.net/")// 此处替换为为你的app服务器host主机地址                  .build();      EasyPay.newInstance(params).requestPayInfo(new OnPayInfoRequestListener() {              @Override              public void onPayInfoRequetStart() {                  // TODO 在此处做一些loading操作,progressbar.show();              }                @Override              public void onPayInfoRequstSuccess() {                  // TODO 可以将loading状态去掉了。请求预支付信息成功,开始跳转到客户端支付。              }                @Override              public void onPayInfoRequestFailure() {                  // / TODO 可以将loading状态去掉了。获取预支付信息失败,会同时得到一个支付失败的回调。              }          }).toPay(new OnPayResultListener() {                @Override              public void onPaySuccess(PayWay payWay) {                  // 支付成功              }                @Override              public void onPayCancel(PayWay payWay) {                  // 支付流程被用户中途取消              }                @Override              public void onPayFailure(PayWay payWay, int errCode) {                  // 支付失败,errCode码详见来源博客或者github项目主页的README文档              }          });</code></pre>    <p>开发者调用步骤:</p>    <ol>     <li>通过建造者模式创建支付参数PayParams实例并传入EasyPay的创建方法中</li>     <li>传入支付结果回调接口实例得到支付结果回调</li>    </ol>    <p>假如你的app中每个商品有id,请求服务器时可以用商品id代替价格,让服务器自己去查询价格,防止客户端中的商品价格被恶意修改。</p>    <h2>开发者需要做什么</h2>    <p>上一节是开发者在Activity/Fragment之类的View层调用代码,除此之外,开发者还需要做一些少量的额外的工作。</p>    <p>需要理解移动支付的流程</p>    <ol>     <li>APP将商品信息post给APP服务器</li>     <li>APP服务器携带商品信息和一些其他信息请求支付平台服务器,获取预支付订单信息</li>     <li>APP服务器得到预支付订单信息并返给APP</li>     <li>APP解析预支付订单信息</li>     <li>APP利用解析后的预支付信息调起支付客户端(微信,支付宝,等)</li>     <li>支付客户端将支付结果返给APP</li>     <li>APP向用户展示支付结果</li>    </ol>    <p>需要导入EasyPay/library源码依赖并修改app客户端相关文件</p>    <ol>     <li>下载EasyPay源码到本地</li>     <li>在Android Studio中打开你的app项目</li>     <li>Android Studio左上角File->New->Import Module->... 选择library目录导入,app会自动依赖library这个module</li>     <li>复制 EasyPay/sample 下 wxapi 包到你的包路径下,假如你的包名: com.app.payclient ,那么wxapi包应该放在payclient包下面</li>     <li>按照 EasyPay/sample 的 AndroidMenifest.xml 文件修改你的清单文件</li>     <li>按照 EasyPay/sample 的 proguard-rules.pro 修改你的混淆文件</li>    </ol>    <p>需要修改服务器请求路径和请求字段和返回的json解析</p>    <p>由于笔者并不知道你的服务器地址和请求路径及字段和返回json格式,所以你只需要动动小手改下library中的相应代码即可。</p>    <p>假如你的支付api接口文档如下:</p>    <p>host: <a href="/misc/goto?guid=4959745927436483915" rel="nofollow,noindex">http://api.yourhost.com/</a></p>    <p>路径:pay/</p>    <p>请求方式为:Http,get</p>    <p>请求需要的参数字段为:</p>    <p><img src="https://simg.open-open.com/show/1a090e40e288899bf1dfbf10fa3c280a.png"></p>    <p>假设你想使用的网络框架是 <strong>Retrofit2</strong> 。那么 network/NetwrokClientInterf 的实现类RetrofitClient需要做如下修改:</p>    <p>由于Retrofit请求一般需要借助一个xxService类,那么实际修改的是xxService类,以EasyPay源码中的 network/PrePayInfoService 为例,它需要修改成如下:</p>    <pre>  <code class="language-java">public interface PrePayInfoService {      @GET("pay/")      Call<ResponseBody> getPrePayInfo(@Query("pay_way")String payWay, @Query("price") String GoddsPrice, @Query("goods_name") String goodsName, @Query(("goods_introduction")) String goodsIntroduce);       /*@POST("pay/")      Call<ResponseBody> postPrePayInfo(@Query("pay_way")String payWay, @Query("price") String GoddsPrice, @Query("goods_name") String goodsName, @Query(("goods_introduction")) String goodsIntroduce);*/  }  // (如需更多字段请自行添加参数)</code></pre>    <p>当网络连接使用其他框架时,需要在NetworkClientInterf对应的实现类中修改路径及请求参数字段。</p>    <p>假如当前用户使用了 <strong>微信支付</strong> ,当服务器返回的数据格式如下时:</p>    <p><img src="https://simg.open-open.com/show/be05286d007db1ec8766ca3ba5d15069.png"></p>    <p>假如json的格式如下:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/72ba782d218845b6621db8955ac4e8eb.png"></p>    <p>以你们server端人员给出的实际json字段来修改 pay/PrePayInfo</p>    <p>。</p>    <p>当为其他支付方式时,也需要在对应的PayStragetyInterf支付实现策略类中修改解析。</p>    <p>library源码中需要修改的地方都打上了 TODO 标签,导入Android Studio后,如下图方式查看:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/9241ac4374bc8b37aadb26d6d74a148d.png"></p>    <p>TODO</p>    <h2>开发者能做什么</h2>    <p>EasyPay目前实现了微信,支付宝app支付,如果你觉得支付逻辑代码不OK?完全可以通过实现PayStragetyInterf来完全重写一个自己的微信,支付宝支付策略。</p>    <p>EasyPay支持的平台(微信,支付宝,银联)不在你的需求范围内?可以通过实现PayStragetyInterf来扩展一种支付方式。</p>    <p>EasyPay支持的网络框架(HttpUrlConnection,OkHttp3(前两者严格意义上不属于框架),Volley,Retrofit2)用的不顺手?那就自己撸一个NetworkClientInterf接口的实现类来实现自己的网络请求客户端。</p>    <p>其他,还是不够满足你的需求,欢迎提出issue,或者加入一起开发,完善该repo,打造一个更加优秀的EasyPay。</p>    <h2>后记</h2>    <p>EasyPay算笔者的第一个正式的开源项目吧,受益于开源社区,也希望为开源奉献一点力量,帮助一些开发者快速打造一个无风险的功能完备的支付框架。</p>    <h2>EasyPay项目地址</h2>    <p><a href="/misc/goto?guid=4959745927359024803" rel="nofollow,noindex">https://github.com/xiong-it/EasyPay</a></p>    <h2>EasyPay的回调errCode错误码列表</h2>    <table>     <thead>      <tr>       <th><strong>通用errCode</strong></th>       <th><strong>意义</strong></th>      </tr>     </thead>     <tbody>      <tr>       <td>1</td>       <td>当前网络无连接(尚未进入支付阶段)</td>      </tr>      <tr>       <td>2</td>       <td>请求APP服务器超时(尚未进入支付阶段)</td>      </tr>      <tr>       <td>-1</td>       <td>支付失败-原因未知,需要开发者手动排查</td>      </tr>      <tr>       <td><strong>微信errCode</strong></td>       <td>一般不会碰到</td>      </tr>      <tr>       <td>-3</td>       <td>微信接收支付请求失败</td>      </tr>      <tr>       <td>-4</td>       <td>微信支付认证失败,拒绝支付交易</td>      </tr>      <tr>       <td>-5</td>       <td>微信版本低,不支持交易</td>      </tr>      <tr>       <td>-6</td>       <td>微信拒绝了支付交易</td>      </tr>      <tr>       <td>-7</td>       <td>未安装微信客户端,交易失败</td>      </tr>      <tr>       <td><strong>支付宝errCode</strong></td>       <td>一般不会碰到</td>      </tr>      <tr>       <td>8000</td>       <td>支付结果待确认,生成了交易订单,但是未支付。</td>      </tr>      <tr>       <td>6002</td>       <td>网络差导致支付失败</td>      </tr>      <tr>       <td>6004</td>       <td>支付结果未知</td>      </tr>      <tr>       <td>6005</td>       <td>支付失败,原因未知</td>      </tr>     </tbody>    </table>    <p> </p>    <p> </p>    <p>来自:https://juejin.im/post/58d242232f301e007e63bce9</p>    <p> </p>