支付流程

1. 前端选择商品,调用paypal支付

2. 支付完成,paypal会返回payment信息,将payment的id返回给自己服务端

3. 服务端根据paymentId,调paypal,查询支付是否完成

准备

  • 前往https://www.paypal.com/注册商家账号

  • 前往https://developer.paypal.com/,使用刚注册的商家账号登录

  • 点击右上角名字,选择Dashboard

  • 在左边的导航栏中点击 Sandbox 下的 Accounts

  • 进入Acccouts界面后,可以看到系统有两个已经生成好的测试账号,但是我们不要用系统给的测试账号,自己创建两个,一个是商家账号,一个是个人账号

    1. 点击右上角的“Create Account”,创建测试用户
    2. 先创建一个“ PERSONAL”类型的用户,国家一定要选“China”,账户余额自己填写(邮箱地址可以随便写,自己好记就行)
    3. 接着创建一个“BUSINESS”类型的用户,国家一定要选“China”,账户余额自己填写 (邮箱地址可以随便写,自己好记就行)
    4. 创建好之后可以点击测试账号下的”Profile“,可以查看信息,如果没加载出来,刷新
    5. 用测试账号登录测试网站查看,注意!这跟paypal官网不同!不是同一个地址,在浏览器输入:https://www.sandbox.paypal.com 在这里登陆测试账户
  • 创建应用,生成用于测试的clientID 和 密钥

    1. 点击左边导航栏Dashboard下的My Apps & Credentials,创建一个Live账号
    2. 然后再到下边创建App
    3. 点击刚刚创建好的App,注意看到”ClientID“ 和”Secret“(Secret如果没显示,点击下面的show就会看到,点击后show变为hide)

start coding

Android`

  • 添加依赖

    `compile('com.paypal.sdk:paypal-android-sdk:2.15.1') 
    { exclude group: 'io.card' }//禁止通过信用卡直接支付,如果不禁止可以直接去掉这一句`
  • 强制在你的编译版本上执行编译操作(编译不过的时候才建议添加)

    `<uses-sdk    android:minSdkVersion="这里填写你需要的编译版本"    tools:overrideLibrary="com.paypal.android.sdk.payments" />`
  • 封装好的工具类

    `
        import android.app.Activity;
        import android.content.Context;
        import android.content.Intent;
        import android.util.Log;
    
        import com.globalegrow.app.gearbest.network.HttpCallBack;
        import com.globalegrow.app.gearbest.widget.CustomToast;
        import com.paypal.android.sdk.payments.PayPalAuthorization;
        import com.paypal.android.sdk.payments.PayPalConfiguration;
        import com.paypal.android.sdk.payments.PayPalFuturePaymentActivity;
        import com.paypal.android.sdk.payments.PayPalItem;
        import com.paypal.android.sdk.payments.PayPalPayment;
        import com.paypal.android.sdk.payments.PayPalPaymentDetails;
        import com.paypal.android.sdk.payments.PayPalProfileSharingActivity;
        import com.paypal.android.sdk.payments.PayPalService;
        import com.paypal.android.sdk.payments.PaymentActivity;
        import com.paypal.android.sdk.payments.PaymentConfirmation;
    
        import org.json.JSONException;
        import org.json.JSONObject;
    
        import java.io.IOException;
        import java.math.BigDecimal;
    
        /**
         * Create by Moosen on 09/11/2016
         */
        public class PayPalHelper {
    
            private static final String TAG = "PayPalHelper";
            //配置何种支付环境,一般沙盒,正式
            private static final String CONFIG_ENVIRONMENT = PayPalConfiguration.ENVIRONMENT_SANDBOX;
    
            // note that these credentials will differ between live & sandbox environments.
            //你所注册的APP Id
            private static final String CONFIG_CLIENT_ID = "";
    
            private static final int REQUEST_CODE_PAYMENT = 1;
            private static final int REQUEST_CODE_FUTURE_PAYMENT = 2;
            private static final int REQUEST_CODE_PROFILE_SHARING = 3;
    
            private static PayPalConfiguration config = new PayPalConfiguration()
                    .environment(CONFIG_ENVIRONMENT)
                    .clientId(CONFIG_CLIENT_ID);
            //以下配置是授权支付的时候用到的
        //            .merchantName("Example Merchant")
        //            .merchantPrivacyPolicyUri(Uri.parse("https://www.example.com/privacy"))
        //            .merchantUserAgreementUri(Uri.parse("https://www.example.com/legal"));
            private static PayPalHelper payPalHelper;
    
            private PayPalHelper() {
            }
    
            public static PayPalHelper getInstance() {
                if (payPalHelper == null) {
                    synchronized (PayPalHelper.class) {
                        payPalHelper = new PayPalHelper();
                    }
                }
                return payPalHelper;
            }
    
            /**
             * 启动PayPal服务
             *
             * @param context
             */
            public void startPayPalService(Context context) {
                Intent intent = new Intent(context, PayPalService.class);
                intent.putExtra(PayPalService.EXTRA_PAYPAL_CONFIGURATION, config);
                context.startService(intent);
            }
    
            /**
             * 停止PayPal服务  sdfsdfsdssaaass
             *
             * @param context
             */
            public void stopPayPalService(Context context) {
                context.stopService(new Intent(context, PayPalService.class));
            }
    
            /**
             * 开始执行支付操作
             *
             * @param context
             */
            public void doPayPalPay(Context context) {
                /*
                 * PAYMENT_INTENT_SALE will cause the payment to complete immediately.
                 * Change PAYMENT_INTENT_SALE to
                 *   - PAYMENT_INTENT_AUTHORIZE to only authorize payment and capture funds later.
                 *   - PAYMENT_INTENT_ORDER to create a payment for authorization and capture
                 *     later via calls from your server.
                 *
                 * Also, to include additional payment details and an item list, see getStuffToBuy() below.
                 */
                PayPalPayment thingToBuy = getStuffToBuy(PayPalPayment.PAYMENT_INTENT_SALE);
                /*
                 * See getStuffToBuy(..) for examples of some available payment options.
                 */
                Intent intent = new Intent(context, PaymentActivity.class);
    
                // send the same configuration for restart resiliency
                intent.putExtra(PayPalService.EXTRA_PAYPAL_CONFIGURATION, config);
    
                intent.putExtra(PaymentActivity.EXTRA_PAYMENT, thingToBuy);
    
                ((Activity) context).startActivityForResult(intent, REQUEST_CODE_PAYMENT);
            }
    
            /*
                 * This method shows use of optional payment details and item list.
                 *
                 * 直接给PP创建支付的信息,支付对象实体信息
                 */
            private PayPalPayment getStuffToBuy(String paymentIntent) {
                //--- include an item list, payment amount details
                //具体的产品信息列表
                PayPalItem[] items =
                        {
                                new PayPalItem("sample item #1", 2, new BigDecimal("0.50"), "USD",
                                        "sku-12345678"),
                                new PayPalItem("free sample item #2", 1, new BigDecimal("0.00"),
                                        "USD", "sku-zero-price"),
                                new PayPalItem("sample item #3 with a longer name", 6, new BigDecimal("0.99"),
                                        "USD", "sku-33333")
                        };
                BigDecimal subtotal = PayPalItem.getItemTotal(items);
                BigDecimal shipping = new BigDecimal("0.21");
                BigDecimal tax = new BigDecimal("0.67");
                PayPalPaymentDetails paymentDetails = new PayPalPaymentDetails(shipping, subtotal, tax);
                BigDecimal amount = subtotal.add(shipping).add(tax);
                PayPalPayment payment = new PayPalPayment(amount, "USD", "sample item", paymentIntent);
                payment.items(items).paymentDetails(paymentDetails);
                //--- set other optional fields like invoice_number, custom field, and soft_descriptor
                payment.custom("This is text that will be associated with the payment that the app can use.");
                return payment;
            }
    
            /**
             * 处理支付之后的结果
             *
             * @param context
             * @param requestCode
             * @param resultCode
             * @param data
             */
            public void confirmPayResult(final Context context, int requestCode, int resultCode, Intent data, final DoResult doResult) {
                if (requestCode == REQUEST_CODE_PAYMENT) {
                    if (resultCode == Activity.RESULT_OK) {
                        PaymentConfirmation confirm =
                                data.getParcelableExtra(PaymentActivity.EXTRA_RESULT_CONFIRMATION);
                        if (confirm != null) {
                            try {
                                Log.i(TAG, confirm.toJSONObject().toString(4));
                                Log.i(TAG, confirm.getPayment().toJSONObject().toString(4));
    
                                /**
                                 *  TODO: send 'confirm' (and possibly confirm.getPayment() to your server for verification
                                 * or consent completion.
                                 * See https://developer.paypal.com/webapps/developer/docs/integration/mobile/verify-mobile-payment/
                                 * for more details.
                                 *
                                 * For sample mobile backend interactions, see
                                 * https://github.com/paypal/rest-api-sdk-python/tree/master/samples/mobile_backend
                                 */
        //                        displayResultText("PaymentConfirmation info received from PayPal");
                                // 这里直接跟服务器确认支付结果,支付结果确认后回调处理结果
                                JSONObject jsonObject = confirm.toJSONObject();
                                if (jsonObject != null) {
                                    JSONObject response = jsonObject.optJSONObject("response");
                                    if (response != null) {
                                        String id = response.optString("id");
                                        try {
                                            CartManager.getInstance().confirmPayPalPayPrice(context, id, new HttpCallBack<String>() {
                                                @Override
                                                public void onSuccess(String responseString) {
                                                    if (responseString != null) {
                                                        try {
                                                            JSONObject jsonObject = new JSONObject(responseString);
                                                            if (jsonObject != null) {
                                                                int resultcode = jsonObject.optInt("_resultcode");
                                                                String msg = jsonObject.optString("_msg");
                                                                if (200 == resultcode) {
                                                                    doResult.confirmSuccess();
                                                                    Log.i(TAG, "dddddddd");
                                                                } else {
                                                                    Log.i(TAG, "ssssssss");
                                                                    CustomToast.getInstance(context).showToast(msg);
                                                                    doResult.confirmNetWorkError();
                                                                }
                                                                CustomToast.getInstance(context).showToast(msg);
                                                            }
                                                        } catch (JSONException e) {
                                                            e.printStackTrace();
                                                            doResult.confirmNetWorkError();
                                                        }
                                                    }
                                                }
    
                                                @Override
                                                public void onFailure(IOException e) {
                                                    Log.i(TAG, "aaaaaaaa");
                                                    doResult.confirmNetWorkError();
                                                }
                                            });
                                        } catch (Exception e) {
                                            e.printStackTrace();
                                            doResult.confirmNetWorkError();
                                        }
                                    }
                                }
                            } catch (JSONException e) {
                                Log.e(TAG, "an extremely unlikely failure occurred: ", e);
                                doResult.confirmNetWorkError();
                            }
                        }
                    } else if (resultCode == Activity.RESULT_CANCELED) {
                        Log.i(TAG, "The user canceled.");
                        doResult.customerCanceled();
                    } else if (resultCode == PaymentActivity.RESULT_EXTRAS_INVALID) {
                        doResult.invalidPaymentConfiguration();
                        Log.i(
                                TAG,
                                "An invalid Payment or PayPalConfiguration was submitted. Please see the docs.");
                    }
                } else if (requestCode == REQUEST_CODE_FUTURE_PAYMENT) {
                    if (resultCode == Activity.RESULT_OK) {
                        PayPalAuthorization auth =
                                data.getParcelableExtra(PayPalFuturePaymentActivity.EXTRA_RESULT_AUTHORIZATION);
                        if (auth != null) {
                            try {
                                doResult.confirmFuturePayment();
                                Log.i("FuturePaymentExample", auth.toJSONObject().toString(4));
    
                                String authorization_code = auth.getAuthorizationCode();
                                Log.i("FuturePaymentExample", authorization_code);
    
        //                        sendAuthorizationToServer(auth);
        //                        displayResultText("Future Payment code received from PayPal");
    
                            } catch (JSONException e) {
                                doResult.confirmNetWorkError();
                                Log.e("FuturePaymentExample", "an extremely unlikely failure occurred: ", e);
                            }
                        }
                    } else if (resultCode == Activity.RESULT_CANCELED) {
                        Log.i("FuturePaymentExample", "The user canceled.");
                        doResult.customerCanceled();
                    } else if (resultCode == PayPalFuturePaymentActivity.RESULT_EXTRAS_INVALID) {
                        doResult.invalidPaymentConfiguration();
                        Log.i(
                                "FuturePaymentExample",
                                "Probably the attempt to previously start the PayPalService had an invalid PayPalConfiguration. Please see the docs.");
                    }
                } else if (requestCode == REQUEST_CODE_PROFILE_SHARING) {
                    if (resultCode == Activity.RESULT_OK) {
                        PayPalAuthorization auth =
                                data.getParcelableExtra(PayPalProfileSharingActivity.EXTRA_RESULT_AUTHORIZATION);
                        if (auth != null) {
                            try {
                                Log.i("ProfileSharingExample", auth.toJSONObject().toString(4));
    
                                String authorization_code = auth.getAuthorizationCode();
                                Log.i("ProfileSharingExample", authorization_code);
    
        //                        sendAuthorizationToServer(auth);
        //                        displayResultText("Profile Sharing code received from PayPal");
    
                            } catch (JSONException e) {
                                Log.e("ProfileSharingExample", "an extremely unlikely failure occurred: ", e);
                            }
                        }
                    } else if (resultCode == Activity.RESULT_CANCELED) {
                        Log.i("ProfileSharingExample", "The user canceled.");
                    } else if (resultCode == PayPalFuturePaymentActivity.RESULT_EXTRAS_INVALID) {
                        Log.i(
                                "ProfileSharingExample",
                                "Probably the attempt to previously start the PayPalService had an invalid PayPalConfiguration. Please see the docs.");
                    }
                }
            }
    
            /**
             * c处理完结果之后回调
             */
            public interface DoResult {
                //与服务确认支付成功
                void confirmSuccess();
    
                //网络异常或者json返回有问题
                void confirmNetWorkError();
    
                //用户取消支付
                void customerCanceled();
    
                //授权支付
                void confirmFuturePayment();
    
                //订单支付验证无效
                void invalidPaymentConfiguration();
            }
    
        }
    `
  • 在你需要调起支付的页面配置支付环境(或者在基类配置相应的支付环境)

    `
        //配置何种支付环境,一般沙盒,正式    
        private static final String CONFIG_ENVIRONMENT = PayPalConfiguration.ENVIRONMENT_SANDBOX;    
        // note that these credentials will differ between live & sandbox environments.    
        //你所注册的APP Id    private static final String CONFIG_CLIENT_ID = "你所注册的CLient Id";   
        private static final int REQUEST_CODE_PAYMENT = 1;    
        private static final int REQUEST_CODE_FUTURE_PAYMENT = 2;    
        private static final int REQUEST_CODE_PROFILE_SHARING = 3;    
        private static PayPalConfiguration config = new PayPalConfiguration()            .environment(CONFIG_ENVIRONMENT)            
        .clientId(CONFIG_CLIENT_ID);    
        //以下配置是授权支付的时候用到的
        //.merchantName("Example Merchant")
        // .merchantPrivacyPolicyUri(Uri.parse("https://www.example.com/privacy"))
        //.merchantUserAgreementUri(Uri.parse("https://www.example.com/legal"));
    `
  • 在类的onCreate方法里面调起支付服务

    `
        Intent intent = new Intent(this, PayPalService.class);
        intent.putExtra(PayPalService.EXTRA_PAYPAL_CONFIGURATION, config);
        startService(intent);
    `
  • 创建支付实体,在需要调起支付的地方调起支付

    `
        public void onBuyPressed(View pressed) {
        //创建支付对象,用于传过去给PayPal服务器进行收款
        PayPalPayment thingToBuy = getThingToBuy(PayPalPayment.PAYMENT_INTENT_SALE);
        Intent intent = new Intent(SampleActivity.this, PaymentActivity.class);
        intent.putExtra(PayPalService.EXTRA_PAYPAL_CONFIGURATION, config);
        intent.putExtra(PaymentActivity.EXTRA_PAYMENT, thingToBuy);
        //这里直接调起PayPal的sdk进行付款操作
        startActivityForResult(intent, REQUEST_CODE_PAYMENT);
        }
        //这里只传一个总价格或者单个产品的信息收款情况
        private PayPalPayment getThingToBuy(String paymentIntent) {
        return new PayPalPayment(new BigDecimal("0.01"), "USD", "sample item",
        paymentIntent);
    
        }
        //这里是购买一系列产品创建购买对象
        private PayPalPayment getStuffToBuy(String paymentIntent) {
        PayPalItem[] items =
        {
        new PayPalItem("sample item #1", 2, new BigDecimal("87.50"), "USD",
        "sku-12345678"),
        new PayPalItem("free sample item #2", 1, new BigDecimal("0.00"),
        "USD", "sku-zero-price"),
        new PayPalItem("sample item #3 with a longer name", 6, new BigDecimal("37.99"),
        "USD", "sku-33333")
        };
        BigDecimal subtotal = PayPalItem.getItemTotal(items);
        BigDecimal shipping = new BigDecimal("7.21");
        BigDecimal tax = new BigDecimal("4.67");
        PayPalPaymentDetails paymentDetails = new PayPalPaymentDetails(shipping, subtotal, tax);
        BigDecimal amount = subtotal.add(shipping).add(tax);
        PayPalPayment payment = new PayPalPayment(amount, "USD", "sample item", paymentIntent);
        payment.items(items).paymentDetails(paymentDetails);
        //--- set other optional fields like invoice_number, custom field, and soft_descriptor
        payment.custom("This is text that will be associated with the payment that the app can use.");
        return payment;
        }
    `
  • 在类的onActivityResult 里进行回调结果的处理

    `
    if (resultCode == Activity.RESULT_OK) {
        PaymentConfirmation confirm =
        data.getParcelableExtra(PaymentActivity.EXTRA_RESULT_CONFIRMATION);
        if (confirm != null) {
        try {
        Log.i(TAG, confirm.toJSONObject().toString(4));
        Log.i(TAG, confirm.getPayment().toJSONObject().toString(4));
        //这里可以把PayPal带回来的json数据传给服务器以确认你的款项是否收到或者收全
        //可以直接把 confirm.toJSONObject() 这个带给服务器,
        //得到服务器返回的结果,你就可以跳转成功页面或者做相应的处理了
        } catch (JSONException e) {
        Log.e(TAG, "an extremely unlikely failure occurred: ", e);
        }
        }
        } else if (resultCode == Activity.RESULT_CANCELED) {
        Log.i(TAG, "The user canceled.");
        } else if (resultCode == PaymentActivity.RESULT_EXTRAS_INVALID) {
        Log.i(
        TAG,
        "An invalid Payment or PayPalConfiguration was submitted. Please see the docs.");
        }
        }

    `

  • 在类的onDestroy 注销服务

    `
    stopService(new Intent(this, PayPalService.class));
    `

服务端,根据前端给的paymentId,判断是否完成付款

`
    package com.net.pay.paypal.service;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;

import net.sf.json.JSONArray;
import net.sf.json.JSONObject;

import org.apache.commons.codec.binary.Base64;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.net.crud.dao.RechargeMapper;

@Service
public class PayPalVerifyPayment {


    private static final String TOKEN_URL = "https://api.sandbox.paypal.com/v1/oauth2/token";
    private static final String PAYMENT_DETAIL = "https://api.sandbox.paypal.com/v1/payments/payment/";
    private static final String clientId = "clientId";
    private static final String secret = "secret";

    /**
     * 获取token
     * 了解更多:https://developer.paypal.com/webapps/developer/docs/integration/mobile/verify-mobile-payment/
     * @return
     */
    private String getAccessToken(){
        try{
            URL url = new URL(TOKEN_URL);
            String authorization = clientId+":"+secret;
            authorization = Base64.encodeBase64String(authorization.getBytes());

            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("POST");// 提交模式
            //设置请求头header
            conn.setRequestProperty("Accept", "application/json");
            conn.setRequestProperty("Accept-Language", "en_US");
            conn.setRequestProperty("Authorization", "Basic "+authorization);
            // conn.setConnectTimeout(10000);//连接超时 单位毫秒
            // conn.setReadTimeout(2000);//读取超时 单位毫秒
            conn.setDoOutput(true);// 是否输入参数
            String params = "grant_type=client_credentials";
            conn.getOutputStream().write(params.getBytes());// 输入参数

            InputStreamReader inStream = new InputStreamReader(conn.getInputStream());
            BufferedReader reader = new BufferedReader(inStream);
            StringBuilder result = new StringBuilder();
            String lineTxt = null;
            while((lineTxt = reader.readLine()) != null){
                result.append(lineTxt);
            }
            reader.close();
            String accessTokey = JSONObject.fromObject(result.toString()).optString("access_token");
            System.out.println("getAccessToken:"+accessTokey);
            return accessTokey;
        }catch(Exception err){
            err.printStackTrace();
        }
        return null;
    }
    /**
     * 获取支付详情
     * 了解更多:https://developer.paypal.com/webapps/developer/docs/integration/mobile/verify-mobile-payment/
     * @param paymentId 支付ID,来自于用户客户端
     * @return
     */
    public String getPaymentDetails(String paymentId){
        try{
            URL url = new URL(PAYMENT_DETAIL+paymentId);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("GET");// 提交模式
            //设置请求头header
            conn.setRequestProperty("Accept", "application/json");
            conn.setRequestProperty("Authorization", "Bearer "+getAccessToken());
            // conn.setConnectTimeout(10000);//连接超时 单位毫秒
            // conn.setReadTimeout(2000);//读取超时 单位毫秒
            InputStreamReader inStream = new InputStreamReader(conn.getInputStream());
            BufferedReader reader = new BufferedReader(inStream);
            StringBuilder result = new StringBuilder();
            String lineTxt = null;
            while((lineTxt = reader.readLine()) != null){
                result.append(lineTxt);
            }
            reader.close();
            return result.toString();
        }catch(Exception err){
            err.printStackTrace();
        }
        return null;
    }
    /**
     * 获取支付详情
     * 了解更多:https://developer.paypal.com/webapps/developer/docs/integration/mobile/verify-mobile-payment/
     * @param paymentId 支付ID,来自于用户客户端
     * @return
     */
    public boolean verifyPayment(String paymentId) throws Exception {
        String str = getPaymentDetails(paymentId);
        System.out.println(str);
        JSONObject detail = JSONObject.fromObject(str);
        //校验订单是否完成
        if("approved".equals(detail.optString("state"))){
            JSONObject transactions = detail.optJSONArray("transactions").optJSONObject(0);
            JSONObject amount = transactions.optJSONObject("amount");
            JSONArray relatedResources = transactions.optJSONArray("related_resources");
            //从数据库查询支付总金额与Paypal校验支付总金额
            double total = 0;
            System.out.println("amount.optDouble('total'):"+amount.optDouble("total"));
            if( total != amount.optDouble("total") ){
                return false;
            }
            //校验交易货币类型
            String currency = "USD";
            if( !currency.equals(amount.optString("currency")) ){
                return false;
            }
            //校验每个子订单是否完成
            for (int i = 0,j = relatedResources.size(); i < j; i++) {
                JSONObject sale = relatedResources.optJSONObject(i).optJSONObject("sale");
                if(sale!=null){
                    if( !"completed".equals(sale.optString("state")) ){
                        System.out.println("子订单未完成,订单状态:"+sale.optString("state"));
                    }
                }
            }
            return true;
        }
        return false;
    }

    public static void main(String[] args) {
        PayPalVerifyPayment payment = new PayPalVerifyPayment();
        boolean success = false;
        try {
            success = payment.verifyPayment("PAY-9MTXXXXXXXXXXXXXXXXXMI");
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println(success ? "支付完成" : "支付校验失败");
    }
}

`