博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
集成第三方-支付宝那些事
阅读量:6207 次
发布时间:2019-06-21

本文共 12175 字,大约阅读时间需要 40 分钟。

这两天,在移动APP上集成了支付宝支付功能,费了一些周折,除了其他博客上提到的一些问题,这里分享一下自己的经验

Android客户端代码集成

1、准备

a 注册支付宝商家账号

b 开通移动支付功能
c 生成RSA私钥和公钥,上传自己的公钥给支付宝

2、类库下载

,将alipaysdk.jar放到自己的项目中。

3、代码分析

(我自己将支付集成在一个project,作为android library使用)

/** 支付宝类  */public class Alipay {    private Activity context;// 上下文    private String  TAG = "Alipay";    private OnPayResultListener payResultListener;    // 商户PID    public static final String PARTNER = "*";    // 商户收款账号    public static final String SELLER = "*";    // 商户私钥,pkcs8 格式    public static final String RSA_PRIVATE = "*";    // 支付宝公钥    public static final String RSA_PUBLIC = "";    private static final int SDK_PAY_FLAG = 1;    private Handler mHandler = new Handler() {        @SuppressWarnings("unused")        public void handleMessage(Message msg) {            switch (msg.what) {                case SDK_PAY_FLAG: {                    String result_string = (String) msg.obj;                    Log.w(TAG,result_string);                    PayResult payResult = new PayResult(result_string);                    payResultListener.OnPayResult(payResult);//回调 给 activity;                    break;                }                default:                    break;            }        };    };    public Alipay(Activity activity){        this.context = activity;    }    public OnPayResultListener getPayResultListener() {        return payResultListener;    }    public void setPayResultListener(OnPayResultListener payResultListener) {        this.payResultListener = payResultListener;    }    /** 根据订单信息 请求支付宝*/    public void pay(final String payInfo) {        Runnable payRunnable = new Runnable() {            @Override            public void run() {                // 构造PayTask 对象                PayTask alipay = new PayTask(context);                // 调用支付接口,获取支付结果                Log.w(alipay.getVersion(),payInfo);                String result = alipay.pay(payInfo, true);                Message msg = new Message();                msg.what = SDK_PAY_FLAG;                msg.obj = result;                mHandler.sendMessage(msg);            }        };        Thread payThread = new Thread(payRunnable);        payThread.start();  // 必须异步调用    }    /** call alipay sdk pay. 调用SDK支付  */    public void pay(String subject, String body, String price,String trade_no) {        if (TextUtils.isEmpty(PARTNER) || TextUtils.isEmpty(RSA_PRIVATE) || TextUtils.isEmpty(SELLER)) {            Log.e(TAG,"需要配置PARTNER | RSA_PRIVATE| SELLER");            return;        }        //"测试的商品", "该测试商品的详细描述", "0.01"        String orderInfo = getOrderInfo(subject, body, price,trade_no);        /** 特别注意,这里的签名逻辑需要放在服务端,切勿将私钥泄露在代码中! */        String sign = sign(orderInfo);        try {            sign = URLEncoder.encode(sign, "UTF-8");//仅需对sign 做URL编码        } catch (UnsupportedEncodingException e) {            e.printStackTrace();        }        /** 完整的符合支付宝参数规范的订单信息 */        final String payInfo = orderInfo + "&sign=\"" + sign + "\"&" + getSignType();        pay(payInfo);    }    /** create the order info. 创建订单信息 */    private String getOrderInfo(String subject, String body, String price,String trade_no) {        // 签约合作者身份ID        String orderInfo = "partner=" + "\"" + PARTNER + "\"";        // 签约卖家支付宝账号        orderInfo += "&seller_id=" + "\"" + SELLER + "\"";        // 商户网站唯一订单号        orderInfo += "&out_trade_no=" + "\"" + trade_no + "\"";        // 商品名称        orderInfo += "&subject=" + "\"" + subject + "\"";        // 商品详情        orderInfo += "&body=" + "\"" + body + "\"";        // 商品金额        orderInfo += "&total_fee=" + "\"" + price + "\"";        // 服务器异步通知页面路径        orderInfo += "&notify_url=" + "\"" + "http://notify.msp.hk/notify.htm" + "\"";        // 服务接口名称, 固定值        orderInfo += "&service=\"mobile.securitypay.pay\"";        // 支付类型, 固定值        orderInfo += "&payment_type=\"1\"";        // 参数编码, 固定值        orderInfo += "&_input_charset=\"utf-8\"";        // 设置未付款交易的超时时间        // 默认30分钟,一旦超时,该笔交易就会自动被关闭。        // 取值范围:1m~15d。        // m-分钟,h-小时,d-天,1c-当天(无论交易何时创建,都在0点关闭)。        // 该参数数值不接受小数点,如1.5h,可转换为90m。        orderInfo += "&it_b_pay=\"30m\"";        // extern_token为经过快登授权获取到的alipay_open_id,带上此参数用户将使用授权的账户进行支付        // orderInfo += "&extern_token=" + "\"" + extern_token + "\"";        // 支付宝处理完请求后,当前页面跳转到商户指定页面的路径,可空//        orderInfo += "&return_url=\"m.alipay.com\"";        // 调用银行卡支付,需配置此参数,参与签名, 固定值 (需要签约《无线银行卡快捷支付》才能使用)        // orderInfo += "&paymethod=\"expressGateway\"";        return orderInfo;    }    /** sign the order info. 对订单信息进行签名 */    private String sign(String content) {        return SignUtils.sign(content, RSA_PRIVATE);    }    /* get the sign type we use. 获取签名方式 */    private String getSignType() {        return "sign_type=\"RSA\"";    }        /** get the sdk version. 获取SDK版本号 */    public void getSDKVersion() {        PayTask payTask = new PayTask(context);        String version = payTask.getVersion();    }    /**     * 原生的H5(手机网页版支付切natvie支付) 【对应页面网页支付按钮】     * 没有用到     * @param v     */    public void h5Pay(View v) {        Intent intent = new Intent();//this, H5PayDemoActivity.class);        Bundle extras = new Bundle();        /**         * url是测试的网站,在app内部打开页面是基于webview打开的,demo中的webview是H5PayDemoActivity,         * demo中拦截url进行支付的逻辑是在H5PayDemoActivity中shouldOverrideUrlLoading方法实现,         * 商户可以根据自己的需求来实现         */        String url = "http://m.meituan.com";        // url可以是一号店或者美团等第三方的购物wap站点,在该网站的支付过程中,支付宝sdk完成拦截支付        extras.putString("url", url);        intent.putExtras(extras);//        startActivity(intent);    }}

在这个Alipay中主要有两个方法public void pay(final String payInfo)和public void pay(String subject, String body, String price,String trade_no) ,前者是在后台生成订单信息,后者是在app中生成,然后交给alipaysdk处理,然后反馈数据.关于反馈,我定义了一个类和一个接口,代码如下:

/** 支付反馈接口*/public interface OnPayResultListener {    void OnPayResult(PayResult result);}/** 支付结果*/public class PayResult {    private int PAY_TYPE = 1 ; // 1 alipay    private String resultStatus;    private String result;    private String memo;    public static final int PAY_ALIPAY = 1;    public static final int PAY_WEIXIN = 2;    public PayResult(String rawResult) {        if (TextUtils.isEmpty(rawResult))            return;        if(PAY_TYPE == PAY_ALIPAY){            String[] resultParams = rawResult.split(";");            for (String resultParam : resultParams) {                if (resultParam.startsWith("resultStatus")) {                    resultStatus = gatValue(resultParam, "resultStatus");                }                if (resultParam.startsWith("result")) {                    result = gatValue(resultParam, "result");                }                if (resultParam.startsWith("memo")) {                    memo = gatValue(resultParam, "memo");                }            }        }    }    public boolean isSuccess(){        if(PAY_TYPE == PAY_ALIPAY) {            return resultStatus.endsWith("9000");        }        return false;    }    @Override    public String toString() {        return "resultStatus={" + resultStatus + "};memo={" + memo                + "};result={" + result + "}";    }    /** 对支付宝反馈结果的处理 */    private String gatValue(String content, String key) {        String prefix = key + "={";        return content.substring(content.indexOf(prefix) + prefix.length(),                content.lastIndexOf("}"));    }    public String getResultStatus() {        return resultStatus;    }    public String getMemo() {        return memo;    }    public String getResult() {        return result;    }    public void setResultStatus(String resultStatus) {        this.resultStatus = resultStatus;    }    public void setResult(String result) {        this.result = result;    }    public void setMemo(String memo) {        this.memo = memo;    }}

其中支付宝在APP中的反馈的结果是这样的:

resultStatus={9000};
memo={};
result={ partner="..." &seller_id="..." &out_trade_no="..." &sign="..." }
最后在Activity中添加支付功能

Alipay pay = new Alipay(this);  pay.setPayResultListener(this);  pay.pay(bill_name,bill_body,bill_fee,bill_number);  @Override  public void OnPayResult(PayResult result){ // 支付宝移动端反馈        if(result.isSuccess()){            doTaskAsync(bill_id);//反馈给自己的服务器,订单已支付        }else{            toast("支付未完成");        }  }

如果参数设置正确的话,在android上集成支付宝就顺利完成了


PHP服务器端代码集成

进一步仔细研究,发现支付宝建议我们将RSA签名信息放到后台。这里做了进一步的集成,步骤如下:

  • -> APP提供参数

  • -> 服务器生成支付宝订单信息

  • -> APP将信息提供给alipaysdk

  • -> 支付宝处理支付订单

  • -> 支付宝请求notify_url

  • -> notify_url处理获取支付宝的订单反馈

我们后台使用的是PHP,同样在之前的demo下载中有服务端代码,复制php-uft8文件即可,结构如下:

图片描述

其中,需要将key文件夹的rsa_private_key.pem换成自己生成的RSA密钥即可。

1、配置文件

修改配置文件(该处按照自己习惯有改动,可自行考虑),代码如下:

/** alipay.config.php 支付宝配置文件*/return array(    'partner'               => '***',    'sellid'                => '***',    'private_key_path'      => '/../key/rsa_private_key.pem',    'ali_public_key_path'   => '/../key/alipay_public_key.pem',    'sign_type'             => strtoupper('RSA'),    'input_charset'         => strtolower('utf-8'),    'cacert'                => getcwd().'\\cacert.pem',    'transport'             => 'http',    'notify_url'            => 'http://../Alipay/notify_url.php');

最后需要修改两个重要的文件,一个是支付宝订单信息的生成文件,一个是异步通知文件。

3、订单生成文件

rsaSign($orderInfo,$key_path); //1, 加密 $sign = urlencode($sign); //2. 编码 加密字符串// $return_str = $orderInfo.'&sign="'.$sign.'"&sign_type="RSA"';/** 一开始想在后台一口气全部生成订单信息,发现签名错误,这里应该是urlencode的问题,php和java对urlencode(utf8)的处理可能不一样;但奇怪的是,这里需要php处理一遍urlencode,android再处理一遍urlencode,少了一方,都会报签名错误*/ $info = array(); $info['order'] = $orderInfo; $info['rsa'] = $sign; return $info; }}

3、异步通知文件

verifyNotify(); //计算得出通知验证结果if($verify_result) {//验证成功 //获取支付宝的通知返回参数,可参考技术文档中服务器异步通知参数列表 $out_trade_no = $_POST['out_trade_no']; //商户订单号 $trade_no = $_POST['trade_no']; //支付宝交易号 $trade_status = $_POST['trade_status']; //交易状态 WAIT_BUYER_PAY $fee = $_POST['total_fee']; $buyer = $_POST['buyer_email']; logResult('success -> '.$out_trade_no.','.$trade_no.','.$trade_status.','.$fee.','.$buyer); if($_POST['trade_status'] == 'TRADE_FINISHED') { //判断该笔订单是否在商户网站中已经做过处理 //如果没有做过处理,根据订单号(out_trade_no)在商户网站的订单系统中查到该笔订单的详细,并执行商户的业务程序 //如果有做过处理,不执行商户的业务程序 //注意: //退款日期超过可退款期限后(如三个月可退款),支付宝系统发送该交易状态通知 //请判断请求时的total_fee、seller_id与通知时获取的total_fee、seller_id一致 } else if ($_POST['trade_status'] == 'TRADE_SUCCESS') { //注意: //付款完成后,支付宝系统发送该交易状态通知 //请判断请求时的total_fee、seller_id与通知时获取的total_fee、seller_id一致 $bill = new billApi(); $pay_detail = array(); $pay_detail['number'] = $out_trade_no; $pay_detail['channel_no'] = $trade_no; $pay_detail['fee'] = $fee; $pay_detail['buyer'] = $buyer; $pay_detail['channel'] = 'alipay'; $bill->payDishBill($pay_detail); //处理后台订单的信息和状态 } echo "success"; //请不要修改或删除}else { echo "fail"; // 验证失败 if(empty($_POST)) { logResult('fail-> post null'); } else { logResult('fail-> '.$_POST['trade_status'].$_POST['trade_no']); }}

Android客户端 添加支付

此时,在后台生成数据的方案中,android前端Activity请求代码如下:

doTaskAsync(number,shopname,fee)); //向服务器请求数据    public void onJsonSuccess(String json) { //服务器返回订单信息            Alipay pay = new Alipay(this);                    pay.setPayResultListener(this);                    try {                        JSONObject obj = new JSONObject(json);                        String orderInfo = obj.getString("order");                        String sign =  obj.getString("rsa");                        sign = URLEncoder.encode(sign, "UTF-8");                        String payInfo = orderInfo + "&sign=\"" + sign + "\"&sign_type=\"RSA\"";                        pay.pay(payInfo);                    } catch (Exception e) {                        e.printStackTrace();                    }    }        @Override    public void OnPayResult(PayResult result){ // 支付宝移动端反馈            if(result.isSuccess()){                finish();            }else{                toast("支付未完成");            }    }

吐槽

  1. 向alipaysdk发送的订单数据一定要加“”,否则报错

  2. 签名、和签名处理 一定不能有误,否则报错

  3. 后台代码中,一定要注意文件的路径和权限。(主要是私钥的路径和log.txt的权限)

  4. 特别吐槽:rsa签名,在后台本来可以直接生成订单数据,但只能分别生成订单前部分数据+rsa签名数据,在客户端对rsa签名再进行URLEncoder,才能顺利通过。

转载地址:http://zrzja.baihongyu.com/

你可能感兴趣的文章
移动通信调制技术的进展 转
查看>>
压缩映象原理的一个应用
查看>>
Linux 中文乱码问题
查看>>
Asp.Net之自定义表达式构造器(ExpressionBuilder)
查看>>
修改一行SQL代码 性能提升了100倍
查看>>
Windows下Apache+Tomcat+jsp+php的服务器整合配置经验总结
查看>>
我们为什么需要DTO?
查看>>
scp命令
查看>>
编写高质量代码改善C#程序的157个建议[正确操作字符串、使用默认转型方法、却别对待强制转换与as和is]...
查看>>
C++11-新增正则表达式
查看>>
我的第一个 Mono for Android 应用
查看>>
hadoop安装详解
查看>>
Java框架
查看>>
java提高篇(三十)-----Iterator
查看>>
Java 实现简答的单链表的功能
查看>>
输入两个递增排序的链表,合并这两个链表并使新链表中的结点仍然是按照递增排序的...
查看>>
pjlib深入剖析和使用详解
查看>>
开源 java CMS - FreeCMS2.3 职位管理
查看>>
SimpleDateFormat关于时间类的一些常用处理
查看>>
用了一天的时间,linux下expect实现ssh自己主动登录server记,歧视下网上各种抄来抄去残段子...
查看>>