cordova/ionic alipay 支付宝插件

来源:互联网 发布:斯托克斯矩阵 编辑:程序博客网 时间:2024/04/30 06:52

        最近在做的一个项目中涉及到了支付宝支付。在尝试了网络上的几个插件之后,发现都因为版本太老,支付宝官方提供的接口已经发生了改变,所以无法使用。于是决定自己动手编写一个最新版的支付宝插件,下面我们进入主题。

1、创建插件目录

        新建一个文件夹,作为插件根目录,在根目录中新建两个文件夹,一个为src存放原生代码源文件,一个为www存放插件js文件。另外在根目录下创建plugin.xml文件,用于写入插件配置。

2、编写alipay.js

var exec = require('cordova/exec');var Alipay = {  /**   * 支付宝支付业务   *   * @param price   付款金额   * @param subject 主题   * @param body    详情   * @param tradeNo 订单号   */    pay:function(onSuccess, onError, price, subject, body, tradeNo) {        exec(            onSuccess,            onError,            "AlipayPlugin",            "alipay",            [price, subject, body, tradeNo]        );    }}module.exports = Alipay;

第一个参数为支付成功后的回调方法,第二个参数为失败后的回调方法。

3、编写原生代码

3.1 Andoid原生

首先在src目录下,创建android和ios目录,用于存放两个平台的原生代码及库文件,在这里我用的是支付宝的20161222版本,是截止到本文发布时的最新版本。

去支付宝官网下载最新demo,然后将其中的工具类、jar包以及资源文件拷贝到android目录下,新建一个类命名为AlipayPlugin.java,继承CordovaPlugin,用于存放支付宝的支付逻辑。

package com.beanyon.alipay;import android.annotation.SuppressLint;import android.app.Activity;import android.content.Context;import android.os.Handler;import android.os.Message;import android.text.TextUtils;import android.util.Log;import android.view.View;import android.widget.Toast;import com.alipay.sdk.app.PayTask;import com.beanyon.alipay.util.OrderInfoUtil2_0;import org.apache.cordova.CallbackContext;import org.apache.cordova.CordovaPlugin;import org.json.JSONArray;import org.json.JSONException;import java.util.Map;/** * Created by BeanYon on 2017/1/6. */public class AlipayPlugin extends CordovaPlugin {  private Context mContext = null;  private final String ACTION_FLAG = "alipay";//传入的action参照  CallbackContext currentCallbackContext;//回调参数  /**   * 支付宝支付业务:入参app_id   */  public static final String APPID = "你的app_id";  /** 商户私钥,pkcs8格式 */  /** 如下私钥,RSA2_PRIVATE 或者 RSA_PRIVATE 只需要填入一个 */  /** 如果商户两个都设置了,优先使用 RSA2_PRIVATE */  /** RSA2_PRIVATE 可以保证商户交易在更加安全的环境下进行,建议使用 RSA2_PRIVATE */  /** 获取 RSA2_PRIVATE,建议使用支付宝提供的公私钥生成工具生成, */  /**   * 工具地址:https://doc.open.alipay.com/docs/doc.htm?treeId=291&articleId=106097&docType=1   */  public static final String RSA2_PRIVATE = "your_private_key";  public static final String RSA_PRIVATE = "";  private static final int SDK_PAY_FLAG = 1;  @Override  public boolean execute(String action, final JSONArray args, final CallbackContext callbackContext) throws JSONException {    currentCallbackContext = callbackContext;    mContext = cordova.getActivity();    if (ACTION_FLAG.equals(action)) {      return payV2(args.getString(0),args.getString(1),args.getString(2),args.getString(3));//调用支付宝付款    }    return true;  }  @SuppressLint("HandlerLeak")  private Handler mHandler = new Handler() {    @SuppressWarnings("unused")    public void handleMessage(Message msg) {      switch (msg.what) {        case SDK_PAY_FLAG: {          @SuppressWarnings("unchecked")          PayResult payResult = new PayResult((Map<String, String>) msg.obj);          /**           *对于支付结果,请商户依赖服务端的异步通知结果。同步通知结果,仅作为支付结束的通知。           */          String resultInfo = payResult.getResult();// 同步返回需要验证的信息          String resultStatus = payResult.getResultStatus();          // 判断resultStatus 为9000则代表支付成功          if (TextUtils.equals(resultStatus, "9000")) {            // 该笔订单是否真实支付成功,需要依赖服务端的异步通知。            Toast.makeText(mContext, "支付成功", Toast.LENGTH_SHORT).show();            currentCallbackContext.success(resultStatus);          } else {            // 该笔订单真实的支付结果,需要依赖服务端的异步通知。            Toast.makeText(mContext, "支付失败", Toast.LENGTH_SHORT).show();          }          break;        }        default:          break;      }    }    ;  };  /**   * 支付宝支付业务   *   * @param price   付款金额   * @param subject 主题   * @param body    详情   * @param tradeNo 订单号   */  public boolean payV2(String price, String subject, String body, String tradeNo) {    /**     * 这里只是为了方便直接向商户展示支付宝的整个支付流程;所以Demo中加签过程直接放在客户端完成;     * 真实App里,privateKey等数据严禁放在客户端,加签过程务必要放在服务端完成;     * 防止商户私密数据泄露,造成不必要的资金损失,及面临各种安全风险;     *     * orderInfo的获取必须来自服务端;     */    boolean rsa2 = (RSA2_PRIVATE.length() > 0);    Map<String, String> params = OrderInfoUtil2_0.buildOrderParamMap(APPID, rsa2, price, subject, body, tradeNo);    String orderParam = OrderInfoUtil2_0.buildOrderParam(params);    String privateKey = rsa2 ? RSA2_PRIVATE : RSA_PRIVATE;    String sign = OrderInfoUtil2_0.getSign(params, privateKey, rsa2);    final String orderInfo = orderParam + "&" + sign;    Runnable payRunnable = new Runnable() {      @Override      public void run() {        PayTask alipay = new PayTask((Activity) mContext);        Map<String, String> result = alipay.payV2(orderInfo, true);        Log.i("msp", result.toString());        Message msg = new Message();        msg.what = SDK_PAY_FLAG;        msg.obj = result;        mHandler.sendMessage(msg);      }    };    Thread payThread = new Thread(payRunnable);    payThread.start();    return true;  }}
adnroid目录下的文件结构如下图所示

3.2 iOS原生

同样在插件的ios目录下,将支付宝官方提供的demo中必要的库文件以及支持工具类拷贝进去,官方的openssl文件夹引入的时候出现了一些问题,所以我把这个文件夹以framework的形式引入。必要的库文件在这里不赘述,大家下载官方demo就可以看到了。以下同样是iOS插件类的源码,里面调用了支付逻辑。

AlipayPlugin.h

#import <Foundation/Foundation.h>#import <Cordova/CDV.h>#import <Cordova/CDVPlugin.h>@interface AlipayPlugin : CDVPlugin- (void)alipay:(CDVInvokedUrlCommand*)command;@end

AlipayPlugin.m

#import "AlipayPlugin.h"#import "Order.h"#import "RSADataSigner.h"#import <AlipaySDK/AlipaySDK.h>@implementation AlipayPlugin- (void)alipay:(CDVInvokedUrlCommand *)command{//重要说明    //这里只是为了方便直接向商户展示支付宝的整个支付流程;所以Demo中加签过程直接放在客户端完成;    //真实App里,privateKey等数据严禁放在客户端,加签过程务必要放在服务端完成;    //防止商户私密数据泄露,造成不必要的资金损失,及面临各种安全风险;/*============================================================================*//*=======================需要填写商户app申请的===================================*//*============================================================================*/    NSString *appID = @"app_id";        // 如下私钥,rsa2PrivateKey 或者 rsaPrivateKey 只需要填入一个    // 如果商户两个都设置了,优先使用 rsa2PrivateKey    // rsa2PrivateKey 可以保证商户交易在更加安全的环境下进行,建议使用 rsa2PrivateKey    // 获取 rsa2PrivateKey,建议使用支付宝提供的公私钥生成工具生成,    // 工具地址:https://doc.open.alipay.com/docs/doc.htm?treeId=291&articleId=106097&docType=1    NSString *rsa2PrivateKey = @"your_private_key";    NSString *rsaPrivateKey = @"";/*============================================================================*//*============================================================================*//*============================================================================*//*     *获取插件传参     */NSString* price = [command.arguments objectAtIndex:0];// NSLog([@"BeanYon:" stringByAppendingString:myarg]);NSString* subject = [command.arguments objectAtIndex:1];// NSLog([@"BeanYon:" stringByAppendingString:myarg2]);NSString* body = [command.arguments objectAtIndex:2];// NSLog([@"BeanYon:" stringByAppendingString:myarg3]);NSString* tradeNo = [NSString stringWithFormat:@"%d", [command.arguments objectAtIndex:3]];// NSLog([@"BeanYon:" stringByAppendingString:[NSString stringWithFormat:@"%d", [command.arguments objectAtIndex:3]]]);        /*     *生成订单信息及签名     */    //将商品信息赋予AlixPayOrder的成员变量    Order* order = [Order new];        // NOTE: app_id设置    order.app_id = appID;        // NOTE: 支付接口名称    order.method = @"alipay.trade.app.pay";        // NOTE: 参数编码格式    order.charset = @"utf-8";        // NOTE: 当前时间点    NSDateFormatter* formatter = [NSDateFormatter new];    [formatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];    order.timestamp = [formatter stringFromDate:[NSDate date]];        // NOTE: 支付版本    order.version = @"1.0";        // NOTE: sign_type 根据商户设置的私钥来决定    order.sign_type = (rsa2PrivateKey.length > 1)?@"RSA2":@"RSA";        // NOTE: 商品数据    order.biz_content = [BizContent new];    order.biz_content.body = body;    order.biz_content.subject = subject;    order.biz_content.out_trade_no = tradeNo; //订单ID(由商家自行制定)    order.biz_content.timeout_express = @"30m"; //超时时间设置    order.biz_content.total_amount = price; //商品价格        //将商品信息拼接成字符串    NSString *orderInfo = [order orderInfoEncoded:NO];    NSString *orderInfoEncoded = [order orderInfoEncoded:YES];    NSLog(@"orderSpec = %@",orderInfo);        // NOTE: 获取私钥并将商户信息签名,外部商户的加签过程请务必放在服务端,防止公私钥数据泄露;    //       需要遵循RSA签名规范,并将签名字符串base64编码和UrlEncode    NSString *signedString = nil;    RSADataSigner* signer = [[RSADataSigner alloc] initWithPrivateKey:((rsa2PrivateKey.length > 1)?rsa2PrivateKey:rsaPrivateKey)];    if ((rsa2PrivateKey.length > 1)) {        signedString = [signer signString:orderInfo withRSA2:YES];    } else {       signedString = [signer signString:orderInfo withRSA2:NO];    }        // NOTE: 如果加签成功,则继续执行支付    if (signedString != nil) {        //应用注册scheme,在AliSDKDemo-Info.plist定义URL types        NSString *appScheme = @"alisdkdemo";                // NOTE: 将签名成功字符串格式化为订单字符串,请严格按照该格式        NSString *orderString = [NSString stringWithFormat:@"%@&sign=%@",                                 orderInfoEncoded, signedString];                // NOTE: 调用支付结果开始支付        [[AlipaySDK defaultService] payOrder:orderString fromScheme:appScheme callback:^(NSDictionary *resultDic) {            NSLog(@"reslut = %@",resultDic);        }];    }}@end

4、插件配置文件

最后,为插件写好配置文件,以便于安装插件的时候将所有的资源文件加入到Android、iOS平台中打包编译。

plugin.xml

<?xml version="1.0" encoding="UTF-8"?><plugin xmlns="http://apache.org/cordova/ns/plugins/1.0"    id="com.beanyon.alipay"    version="0.0.1">    <name>AlipayPlugin</name>    <description>Cordova Alipay</description>    <license>Apache 2.0</license>    <keywords>cordova alipay</keywords>    <js-module name="AlipayPlugin" src="www/alipay.js">        <clobbers target="cordova.plugins.AlipayPlugin"/>    </js-module>    <!-- android -->    <platform name="android">        <config-file target="res/xml/config.xml" parent="/*">            <feature name="AlipayPlugin">                <param name="android-package" value="com.beanyon.alipay.AlipayPlugin" /><!-- 此处根据自己定义的包名修改 -->            </feature>        </config-file>        <config-file target="AndroidManifest.xml" parent="/*">            <uses-permission android:name="android.permission.INTERNET" />            <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />            <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />            <uses-permission android:name="android.permission.READ_PHONE_STATE" />            <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />        </config-file>        <source-file src="src/android/AlipayPlugin.java" target-dir="src/com/beanyon/alipay"/>        <source-file src="src/android/Base64.java" target-dir="src/com/beanyon/alipay"/>        <source-file src="src/android/ExternalFragment.java" target-dir="src/com/beanyon/alipay"/>        <source-file src="src/android/PayResult.java" target-dir="src/com/beanyon/alipay"/>        <source-file src="src/android/SignUtils.java" target-dir="src/com/beanyon/alipay"/>        <source-file src="src/android/util/OrderInfoUtil2_0.java" target-dir="src/com/beanyon/alipay/util"/>        <source-file src="src/android/lib/alipaySdk-20161222.jar" target-dir="libs" />        <source-file src="src/android/lib/android-support-v13.jar" target-dir="libs" />        <source-file src="src/android/xml/pay_external.xml" target-dir="res/layout" />        <source-file src="src/android/xml/pay_main.xml" target-dir="res/layout" />        <source-file src="src/android/drawable/msp_demo_title.png" target-dir="res/drawable" />        <source-file src="src/android/drawable/msp_demo_title_bg.png" target-dir="res/drawable" />    </platform>    <platform name="ios">        <!--ios9不允许http访问,需要使用https访问,添加以下属性使http正常使用 -->        <config-file target="*-Info.plist" parent="NSAppTransportSecurity">                <dict>                <key>NSAllowsArbitraryLoads</key>                <true />            </dict>        </config-file>        <!-- build setting header search path $(SRCROOT)/X5 -->        <header-file src="src/ios/AlipayPlugin.h" />        <source-file src="src/ios/AlipayPlugin.m" />        <header-file src="src/ios/Order.h" />        <source-file src="src/ios/Order.m" />        <!-- Util -->        <header-file src="src/ios/Util/base64.h" />        <source-file src="src/ios/Util/base64.m" />        <header-file src="src/ios/Util/config.h" />        <header-file src="src/ios/Util/NSDataEx.h" />        <source-file src="src/ios/Util/NSDataEx.m" />        <header-file src="src/ios/Util/openssl_wrapper.h" />        <source-file src="src/ios/Util/openssl_wrapper.m" />        <header-file src="src/ios/Util/RSADataSigner.h" />        <source-file src="src/ios/Util/RSADataSigner.m" />        <header-file src="src/ios/Util/RSADataVerifier.h" />        <source-file src="src/ios/Util/RSADataVerifier.m" />        <resource-file src="src/ios/AlipaySDK.bundle"/>        <config-file target="config.xml" parent="/*">            <feature name="AlipayPlugin">                <param name="ios-package" value="AlipayPlugin" />                <param name="onload" value="true" />            </feature>        </config-file>        <source-file src="src/ios/libcrypto.a" framework="true" />        <source-file src="src/ios/libssl.a" framework="true" />        <!-- 手动copy framework bundle 文件 -->        <!-- afnetworking 依赖security.framework -->        <framework src="libsqlite3.0.tbd"/>        <framework src="libz.1.2.5.tbd" />        <framework src="CoreTelephony.framework" />        <framework src="SystemConfiguration.framework" />        <framework src="Security.framework" />        <framework src="libstdc++.6.0.9.tbd" />        <framework src="AssetsLibrary.framework"/>        <framework src="libz.tbd"/>        <framework src="libc++.tbd"/>        <framework src="CoreMotion.framework"/>        <framework src="src/ios/AlipaySDK.framework" custom="true" />        <framework src="src/ios/openssl.framework" custom="true" />    </platform></plugin>

5、调用

cordova.plugins.AlipayPlugin.pay(        function(resultStatus){          alert("调用成功:"+resultStatus);        },         function(){            alert("调用失败");        },        price, subject, body, tradeNo);

欢迎各位一起交流讨论,集思广益。




0 0
原创粉丝点击