由于最近公司业务需求,为了提升用户体验,产品决定把原本直接加载M版的支付方式,直接改成调起PayPal SDK的方式进行支付,从而可以大大提升加载速度,改善体验。虽然后来出于支付安全考虑否决了这个决定,但是在这里还是跟大家来分享一下集成Android的集成PayPal支付,同时简单封装了一下支付的帮助类,希望能帮到大家。
1.注册PayPal开发者账号,并且添加你的APP
首先我们得去到PayPal的开发者平台去注册自己的账号 https://developer.paypal.com ,同时创建自己的APP,获取APP的CLient ID.
2.在自己的工程里添加相应的SDK依赖
把下面一句代码添加到你工程的gradle.bulid里面
compile('com.paypal.sdk:paypal-android-sdk:2.15.1')
{ exclude group: 'io.card' }//禁止通过信用卡直接支付,如果不禁止可以直接去掉这一句
2.强制在你的编译版本上执行编译操作(编译不过的时候才建议添加)
由于PayPal sdk 现在已经升级到了默认最低是minSDKVersion 16 ,所以如果你的编译版本低于这个版本的时候,AS就会提示你编译不通过,并且报错
Error:Execution failed for task ':app:processDebugManifest'.
> Manifest merger failed : uses-sdk:minSdkVersion 14 cannot be smaller than version 16 declared in library [com.paypal.sdk:paypal-android-sdk:2.15.1] /home/moosen/Documents/GearBest/app/build/intermediates/exploded-aar/com.paypal.sdk/paypal-android-sdk/2.15.1/AndroidManifest.xml
Suggestion: use tools:overrideLibrary="com.paypal.android.sdk.payments" to force usage
这时你需要在清单文件AndroidManifest.xml里面添加一下代码强制它在你需要的版本下编译
当然你也可以把你的minSDKVersion改到16或者更大
3.在你需要调起支付的页面配置支付环境(或者在基类配置相应的支付环境)
//配置何种支付环境,一般沙盒,正式
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"));
4.在类的onCreate方法里面调起支付服务
Intent intent = new Intent(this, PayPalService.class);
intent.putExtra(PayPalService.EXTRA_PAYPAL_CONFIGURATION, config);
startService(intent);
5.创建支付实体,在需要调起支付的地方调起支付
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;
}
6.在类的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.");
}
}
这里授权支付就不做解析和分析了
7.在类的onDestroy 注销服务
stopService(new Intent(this, PayPalService.class));
7.最后,这里简单的封装了一个PayPalHelper帮助类
你只需要在相应的生命周期里面调用PayPalHelper里面的方法,就可以做相应的处理
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() {
@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();
}
}