一、了解Stripe支付
1,最近公司正在做一个国际版APP,涉及到海外支付,调研过Paypal、Skrill、BrainTree、Stripe(可参考海外移动支付方案对比),最终 选择了Stripe支付。Stripe特点如下:
收费规则简单透明,手续费就是收取订单总额的3.4 % + HK$2.35。没有月费、开户费、退款手续费,撤销付款费用手续费HK$85.00
2,Stripe支持135+种货币创建付款(目前不支持中国大陆,只支持中国香港)。
3,Stripe还支持其他付款方式,包括ACH信用转账、ACH借记转账、支付宝、Android Pay、Apple Pay、Bancontact、Bitcoin(比特币)、银行卡(Visa,Mastercard,American Express,Discover,Diners Club,JCB等)、Giropay、iDEAL、SEPA、SOFORT、微信支付等来自全球的热门支付方式。
4, Stripe的开发文档清晰简单,集成友好。提供了IOS、Android的SDK,以及对各种语言的支持。
Android代码:
1,集成SDK
dependencies {
// ...
// Stripe Android SDK
implementation 'com.stripe:stripe-android:20.15.1'
}2,代码
MyApplication代码
package com.example.demo;
import android.app.Application;
import com.stripe.android.PaymentConfiguration;
public class MyApp extends Application {
@Override
public void onCreate() {
super.onCreate();
PaymentConfiguration.init(
getApplicationContext(),
"pk_test_TYooMQauvdEDq54NiTphI7jx"
);
}
}Activity代码:
package com.example.demo;
import android.content.res.ColorStateList;
import android.graphics.Color;
import android.util.Log;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import com.stripe.android.paymentsheet.PaymentSheet;
import com.stripe.android.paymentsheet.PaymentSheetResult;
import okhttp3.*;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
public class CheckoutActivity extends AppCompatActivity {
private static final String TAG = "CheckoutActivity";
private static final String BACKEND_URL = "http://10.0.2.2:4242";
private String paymentIntentClientSecret;
private PaymentSheet paymentSheet;
private Button payButton;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_checkout);
// Hook up the pay button
payButton = findViewById(R.id.pay_button);
payButton.setOnClickListener(this::onPayClicked);
payButton.setEnabled(false);
paymentSheet = new PaymentSheet(this, this::onPaymentSheetResult);
fetchPaymentIntent();
}
private void showAlert(String title, @Nullable String message) {
runOnUiThread(() -> {
AlertDialog dialog = new AlertDialog.Builder(this)
.setTitle(title)
.setMessage(message)
.setPositiveButton("Ok", null)
.create();
dialog.show();
});
}
private void showToast(String message) {
runOnUiThread(() -> Toast.makeText(this, message, Toast.LENGTH_LONG).show());
}
private void fetchPaymentIntent() {
final String shoppingCartContent = "{\"items\": [ {\"id\":\"xl-tshirt\"}]}";
final RequestBody requestBody = RequestBody.create(
shoppingCartContent,
MediaType.get("application/json; charset=utf-8")
);
Request request = new Request.Builder()
.url(BACKEND_URL + "/create-payment-intent")
.post(requestBody)
.build();
new OkHttpClient()
.newCall(request)
.enqueue(new Callback() {
@Override
public void onFailure(@NonNull Call call, @NonNull IOException e) {
showAlert("Failed to load data", "Error: " + e.toString());
}
@Override
public void onResponse(
@NonNull Call call,
@NonNull Response response
) throws IOException {
if (!response.isSuccessful()) {
showAlert(
"Failed to load page",
"Error: " + response.toString()
);
} else {
final JSONObject responseJson = parseResponse(response.body());
paymentIntentClientSecret = responseJson.optString("clientSecret");
runOnUiThread(() -> payButton.setEnabled(true));
Log.i(TAG, "Retrieved PaymentIntent");
}
}
});
}
private JSONObject parseResponse(ResponseBody responseBody) {
if (responseBody != null) {
try {
return new JSONObject(responseBody.string());
} catch (IOException | JSONException e) {
Log.e(TAG, "Error parsing response", e);
}
}
return new JSONObject();
}
private void onPayClicked(View view) {
PaymentSheet.Configuration configuration = new PaymentSheet.Configuration("Example, Inc.");
// Present Payment Sheet
paymentSheet.presentWithPaymentIntent(paymentIntentClientSecret, configuration);
}
private void onPaymentSheetResult(
final PaymentSheetResult paymentSheetResult
) {
if (paymentSheetResult instanceof PaymentSheetResult.Completed) {
showToast("Payment complete!");
} else if (paymentSheetResult instanceof PaymentSheetResult.Canceled) {
Log.i(TAG, "Payment canceled!");
} else if (paymentSheetResult instanceof PaymentSheetResult.Failed) {
Throwable error = ((PaymentSheetResult.Failed) paymentSheetResult).getError();
showAlert("Payment failed", error.getLocalizedMessage());
}
}
}xml文件
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:context=".CheckoutActivity"
tools:showIn="@layout/activity_checkout">
<Button
android:id="@+id/pay_button"
android:text="Pay now"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
简不简单,
看效果:
