Paypal集成准备工作

  1. 登录或注册账号,注册新账号需要选择国家,最好绑定一张银行卡,储蓄卡或信用卡均可
  2. 登录进去在Dashboard界面左边的菜单“My apps & credentials”里面的“REST API apps”项创建一个App或修改一个已有的App,目前系统会默认创建一个Default Application,可直接点击进去修改使用

  3. 点进去之后主要有3大块:

1).SANDBOX API CREDENTIALS:配置信息相关;主要包含Sandbox account(沙箱环境商家收款邮箱账号),Client ID(后台Api接口认证加签和App的Sdk初始化需要用到),Secret(后台Api接口认证加签需要用到)

2).SANDBOX APP SETTINGS:App的Sdk相关配置设置;主要关注Return URL(自定义协议Uri,这个不是支付宝、微信支付完成的回调Url,这个是调起支付需要登录Paypal买家账号时,Paypal会跳转到浏览器进行网页登录,当登录完成需要返回App的时候就需要自定义协议Uri返回我们App里面,格式类类似于网址:com.xxxx.protocol://xxxx.xxx,目前Paypal固定后缀格式:xxx.xxx.xx://paypalpay,一般xxx.xxx.xx设置为当前App的包名);其他需要关注的是Accept payments,必须勾上,且Advanced options展开Native Checkout SDK必须是绿色的勾勾;然后Log in with PayPal,必须勾上,且Advanced options展开Full name和Email必须勾上,Privacy policy URL和User agreement URL随便填写给地址就行,比如百度www.baidu.com/;总体参数按我如下截图… 3).SANDBOX WEBHOOKS:这个就是支付完成Paypal后台通知我们自己后台的配置地方,这个才是类似支付宝、微信支付完成的回调Url,一般配置后台的接口,按需使用

  1. 其他需要关注的就是沙箱环境里面的卖家测试账号和买家账号,账号类型Type字段Business标识的就是卖家,如果需要就配置在代码收款商Payee位置,Type字段Personal标识的就是买家,需要在App的Sdk拉起网页付款登录时登录这个账号;点开Manage accounts对应的三个点,选择View/edit account,在弹框中可点击Change password修改系统随机生成的密码成你自己想要的,注意:修改保存完成后并不会显示,但是已经修改成功啦,可用这个密码去做付款账号的登录了(但是换成生产账号测试时遇到一个奇怪现象:我在App的Sdk拉起网页付款登录时登录Personal标识的买家账号时,结果网页一直提示我是登录的卖家账号,不能完成登录付款,换成Business标识的账号登录就成功了,测试环境就必须登录Personal标识的才能付款,因此暂时只能定义成Paypal的Bug了)

Paypal Android SDK集成工作

  • 1.开启网络权限
<manifest xmlns:android="http://schemas.android.com/apk/res/android">...<uses-permission android:name="android.permission.INTERNET" />...</manifest>
复制代码
  • 2.项目根build.gradle文件中配置paypal的仓库
allprojects {repositories {...maven {url  "https://cardinalcommerceprod.jfrog.io/artifactory/android"credentials {// Be sure to add these non-sensitive credentials in order to retrieve dependencies from the private repository.username 'paypal_sgerritz'  //官方文档这里字符串没得单引号,导致报错,差评password 'AKCp8jQ8tAahqpT5JjZ4FRP2mW7GMoFZ674kGqHmupTesKeAY2G8NcmPKLuTxTGkKjDLRzDUQ'  //官方文档这里字符串没得单引号,导致报错,差评}}}
}
复制代码
  • 3.添加Java 8的兼容处理
android {...compileOptions {sourceCompatibility JavaVersion.VERSION_1_8targetCompatibility JavaVersion.VERSION_1_8}kotlinOptions {jvmTarget = "1.8"}
}
复制代码
  • 4.添加Paypal的lib库最新版依赖(注:因Paypal Sdk是用kotlin写的,请提前添加androidx.core:core-ktx和org.jetbrains.kotlin:kotlin-stdlib-jdk7支持)
implementation('com.paypal.checkout:android-sdk:0.5.2') {exclude group: 'com.google.code.gson', module: 'gson'  //这里排除gson是因为和我主项目的gson冲突了,而且paypal依赖的还是比我新的版本,导致我本地报错了;其实还有个重要冲突是okhttp,我们项目用的是3.14.0,paypal用的是4.8.0,因为4.x版本比3.x版本变化比较大,而且还不能排除paypal的4.x版本,会导致paypal初始化就报错,所以我们只有把主项目okhttp升级到最新了}
复制代码

Paypal Android SDK开发工作

  • 1.在Application的onCreate方法做如下Paypal初始化操作
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {//其中clientId需要替换成上面步骤申请到的客户端idPayPalCheckout.setConfig(CheckoutConfig(application, clientId, if (BuildConfig.DEBUG) Environment.SANDBOX else Environment.LIVE, String.format("%s://paypalpay", BuildConfig.APPLICATION_ID), CurrencyCode.USD, UserAction.PAY_NOW, PaymentButtonIntent.CAPTURE, SettingsConfig(BuildConfig.DEBUG, false)))
}else{System.out.println("initPayPal失败,系统版本过低")
}
复制代码
  • 2.Paypal的3种代码集成方式

1). Client-side integration: 客户端集成,主要特点是需要集成一个PayPal自带的支付按钮PayPalButton,这个按钮仅可做少量Paypal固定的UI定制,侵入性比较强,然后创建订单和执行订单捕获逻辑均在App端,适用于不要后台参与的情况

2). Server-side integration: 服务端集成,主要特点是服务端创建订单和执行订单捕获,App端只需接收服务端返回的paypal订单id,调用createOrderActions.set(orderId)设置即可开启订单支付逻辑,适合于主流支付逻辑,后台订单可信可控,我们采用的这种方式

3). Programmatically start the SDK: 客户端集成,主要特点和Client-side integration比较类似,只是不需要集成PayPal自带的支付按钮PayPalButton,然后创建订单和订单捕获逻辑均在App端,适用于不要后台参与的情况

  • 3.我们集成Server-side integration的代码如下(参数paypalOrderId为后台去调paypal的创单接口获取到的订单id)

我们的逻辑就是如下代码示例,当执行PayPalCheckout.start()方法开启支付时,同步开启一个定时器,循环去轮询paypal订单的状态,接口为REST v2订单详情,检测status是否是COMPLETED支付成功了

    private var checkOrderMax: Int = 300//循环检测的最大次数private var checkOrderCount: Int = -1//循环检测的初始次数private var isCheckOrderFlag: Boolean = false//是否检测订单状态中@RequiresApi(Build.VERSION_CODES.M)private fun startPaypal(context: Activity, paypalOrderId: String){PayPalCheckout.start(CreateOrder { createOrderActions -> createOrderActions.set(paypalOrderId) }, null, null,OnCancel {//实测取消订单后没走这个方法,走到了报错,报的returnUrl是空,但我们再paypal后台和sdk初始化均设置了的,而且在手机浏览器登录paypal买家账号后也是可以拉回我们app的,但是它还是会报这个错,无语子,估计还是paypal得bugresetCheckPaypalOrder()System.out.println("用户取消Paypal支付")},OnError { errorInfo ->resetCheckPaypalOrder()System.out.println("==========paypal onError=======ErrorInfo=======>: $errorInfo")System.out.println(if(errorInfo?.reason.isNullOrBlank()) (if(errorInfo?.error?.message.isNullOrBlank())"Paypal支付未知错误" else errorInfo.error.message) else errorInfo!!.reason)})checkOrderCount = 0isCheckOrderFlag = truestartCheckPaypalOrder(context, paypalOrderId)}private fun startCheckPaypalOrder(context: Activity, paypalOrderId: String){System.out.println("======Stephen=======checkPaypalOrder====>Count:$checkOrderCount")if(!isCheckOrderFlag)returnif(checkOrderCount >= checkOrderMax){resetCheckPaypalOrder()System.out.println("Paypal订单支付等待超时")return}//end of ifApiRequestMethod.checkPaypalOrder(paypalOrderId, object : RequestAllCallback<String> {//这个是我们接口调用封装方法,换成你们自己的哈(REST v2订单详情接口:https://developer.paypal.com/api/orders/v2/#orders_get)override fun onSuccess(data: String?) {//接口返回正确json形如:{"paypal_status":"CREATED"}var isContinueLoop = trueval jsonObject = ToolUtils.instance.fromJsonToObj(data)if(null != jsonObject && jsonObject.has("paypal_status")){when(jsonObject.getString("paypal_status")){"APPROVED" -> {//这个状态表示用户已经支付完成,后台开始捕获订单并开始执行确认了,实测这个状态有时差不多得持续1分多钟,快的话也是耗费20多秒,因此添加一个loading逻辑如下,此具体loading显示逻辑更换成你自己实际的loading框,注意loading框得依附在你自己App的当前支付界面上,因为你看到paypal的支付框实质是一个activity,依附在paypal上面会被误关闭if(!ToolUtils.instance.isLoadingShow())ToolUtils.instance.showLoading("确认支付结果中...", appendActivity = context)}"COMPLETED" -> {//这个状态表示后台捕获确认订单完成,也就是这笔订单真正的完成了isContinueLoop = falseresetCheckPaypalOrder()System.out.println("Paypal订单支付成功")}}}//end of ifif(isContinueLoop){ToolUtils.instance.delayExecute(1000L){//这个是我们延时调用封装方法,换成你们自己的哈checkOrderCount++startCheckPaypalOrder(context, paypalOrderId)}}//end of if}override fun onFailure(aliErrorResponse: AliErrorResponse, httpCode: Int): Boolean {ToolUtils.instance.delayExecute(1000L){checkOrderCount++startCheckPaypalOrder(context, paypalOrderId)}return false}})}private fun resetCheckPaypalOrder(){checkOrderCount = 0isCheckOrderFlag = falseToolUtils.instance.closeLoading()//关闭loading框}
复制代码

附上一个流程图

  • 4.说一下我们集成Server-side integration的代码遇到的问题

1).当开始支付时如果没有登录过Paypal买家账号或账号登录过期了,Sdk会拉起外部浏览器进行网页登录,登录成功后会通过returnUrl拉回业务App进行原生App的支付,然而有时又会直接就待在外部浏览器网页进行支付,支付完成后才会拉回业务App;更有情况登录操作是直接拉起的内部浏览器进行操作,具体原因均未可知,知道的铁子可评论区回复哈(PS:外部浏览器网页通过returnUrl拉起App这个操作有时会直接无效,连“是否跳转XX App”这个弹窗都没有,排查N久无效,猜测应该是浏览器屏蔽了网页的频繁弹窗导致的,具体原因也未可知)

2).官方文档写的:【Note: If you're integrating for the first time, we recommend using the REST v2 server-side integration. You must complete the first five steps in a Client-side integration.】,意思是服务端集成前面步骤和客户端集成一样,需要添加PayPal自带的支付按钮PayPalButton,然后调用payPalButton.setup()方法设置订单id,同时也可以覆盖OnApprove()回调方法;然而我实测发现,可以不用集成支付按钮PayPalButton,直接调用PayPalCheckout.start()方法设置订单id即可开启支付流程,只是最好不要去覆盖OnApprove()回调方法,因为这里执行it.orderActions.capture { }捕获状态时,会报上下文异常:

com.paypal.checkout.order.OrderContextNotAvailableException: Tried to retrieve OrderContext before it was created.at com.paypal.checkout.order.OrderContext$Companion.get(OrderContext.kt:27)at com.paypal.checkout.order.UpdateOrderStatusAction$execute$orderContext$1.invokeSuspend(UpdateOrderStatusAction.kt:28)at com.paypal.checkout.order.UpdateOrderStatusAction$execute$orderContext$1.invoke(Unknown Source:10)at kotlinx.coroutines.intrinsics.UndispatchedKt.startUndispatchedOrReturn(Undispatched.kt:91)at kotlinx.coroutines.BuildersKt__Builders_commonKt.withContext(Builders.common.kt:161)at kotlinx.coroutines.BuildersKt.withContext(Unknown Source:1)at com.paypal.checkout.order.UpdateOrderStatusAction.execute(UpdateOrderStatusAction.kt:27)at com.paypal.checkout.order.CaptureOrderAction$execute$2.invokeSuspend(CaptureOrderAction.kt:21)at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:571)at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:738)at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:678)at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:665)
复制代码

3).还发现一个异常问题,当sdk拉起浏览器登录paypal账号或支付时,此时从最近任务列表切换回app,app会回调到OnError方法里面,此时我们的业务支付流程已经结束了,但如果此时仍然回浏览器完成paypal的支付,这时支付完成首先可能会造成一个异常单问题(paypal扣了用户的钱,但是我们后台订单有可能已经关闭导致用户没得到相应的服务),还有一个坑爹的问题,此时拉回业务app时,app会被paypal的sdk搞崩溃,应该是支付服务在AppResume时Sdk回调OnError方法后支付服务被关闭导致的服务状态异常,最关键是还没法拦截处理,再次无语子

2021-12-27 17:51:45.505 E/DEBUG: Back traces starts.
2021-12-27 17:51:45.509 E/DEBUG:     at com.paypal.openid.AuthorizationService.a(Unknown Source:9)
2021-12-27 17:51:45.507 E/DEBUG: java.lang.IllegalStateException: Service has been disposed and rendered inoperable
2021-12-27 17:51:45.510 E/DEBUG:     at com.paypal.openid.AuthorizationService.performTokenRequest(Unknown Source:0)
2021-12-27 17:51:45.509 E/DEBUG:     at com.paypal.openid.AuthorizationService.a(Unknown Source:9)
2021-12-27 17:51:45.511 E/DEBUG:     at com.paypal.openid.AuthorizationService.performTokenRequest(Unknown Source:2)
2021-12-27 17:51:45.510 E/DEBUG:     at com.paypal.openid.AuthorizationService.performTokenRequest(Unknown Source:0)
2021-12-27 17:51:45.513 E/DEBUG:     at com.paypal.authcore.authentication.Authenticator.a(Unknown Source:191)
2021-12-27 17:51:45.511 E/DEBUG:     at com.paypal.openid.AuthorizationService.performTokenRequest(Unknown Source:2)
2021-12-27 17:51:45.514 E/DEBUG:     at com.paypal.authcore.authentication.Authenticator.authenticateForAccessTokenWithDelegate(Unknown Source:86)
2021-12-27 17:51:45.513 E/DEBUG:     at com.paypal.authcore.authentication.Authenticator.a(Unknown Source:191)
2021-12-27 17:51:45.515 E/DEBUG:     at com.paypal.pyplcheckout.flavorauth.ThirdPartyAuth.getUserAccessToken(ThirdPartyAuth.java:149)
2021-12-27 17:51:45.514 E/DEBUG:     at com.paypal.authcore.authentication.Authenticator.authenticateForAccessTokenWithDelegate(Unknown Source:86)
2021-12-27 17:51:45.516 E/DEBUG:     at com.paypal.pyplcheckout.home.viewmodel.MainPaysheetViewModel.logInUser(MainPaysheetViewModel.java:1629)
2021-12-27 17:51:45.515 E/DEBUG:     at com.paypal.pyplcheckout.flavorauth.ThirdPartyAuth.getUserAccessToken(ThirdPartyAuth.java:149)
2021-12-27 17:51:45.517 E/DEBUG:     at com.paypal.pyplcheckout.home.viewmodel.MainPaysheetViewModel.$r8$lambda$u3bJQV5CM255mmoJkHAH5Y7Tgzs(Unknown Source:0)
2021-12-27 17:51:45.516 E/DEBUG:     at com.paypal.pyplcheckout.home.viewmodel.MainPaysheetViewModel.logInUser(MainPaysheetViewModel.java:1629)
2021-12-27 17:51:45.518 E/DEBUG:     at com.paypal.pyplcheckout.home.viewmodel.MainPaysheetViewModel$$ExternalSyntheticLambda9.onUpdateClientConfig(Unknown Source:2)
2021-12-27 17:51:45.517 E/DEBUG:     at com.paypal.pyplcheckout.home.viewmodel.MainPaysheetViewModel.$r8$lambda$u3bJQV5CM255mmoJkHAH5Y7Tgzs(Unknown Source:0)
2021-12-27 17:51:45.519 V/nxoBaseCallback: https://www.paypal.com/xoplatform/logger/api/logger returned with response
2021-12-27 17:51:45.518 E/DEBUG:     at com.paypal.pyplcheckout.home.viewmodel.MainPaysheetViewModel$$ExternalSyntheticLambda9.onUpdateClientConfig(Unknown Source:2)
2021-12-27 17:51:45.520 E/DEBUG:     at com.paypal.pyplcheckout.home.viewmodel.MainPaysheetViewModel.lambda$updateClientConfigBefore$22(MainPaysheetViewModel.java:818)
2021-12-27 17:51:45.519 V/nxoBaseCallback: https://www.paypal.com/xoplatform/logger/api/logger returned with response
2021-12-27 17:51:45.521 E/DEBUG:     at com.paypal.pyplcheckout.home.viewmodel.MainPaysheetViewModel$$ExternalSyntheticLambda0.onEvent(Unknown Source:2)
2021-12-27 17:51:45.520 E/DEBUG:     at com.paypal.pyplcheckout.home.viewmodel.MainPaysheetViewModel.lambda$updateClientConfigBefore$22(MainPaysheetViewModel.java:818)
2021-12-27 17:51:45.522 E/DEBUG:     at com.paypal.pyplcheckout.events.Events.fire(Events.java:116)
2021-12-27 17:51:45.521 E/DEBUG:     at com.paypal.pyplcheckout.home.viewmodel.MainPaysheetViewModel$$ExternalSyntheticLambda0.onEvent(Unknown Source:2)
2021-12-27 17:51:45.523 E/DEBUG:     at com.paypal.pyplcheckout.services.callbacks.ClientConfigUpdateCallback.onApiSuccess(ClientConfigUpdateCallback.kt:54)
2021-12-27 17:51:45.522 E/DEBUG:     at com.paypal.pyplcheckout.events.Events.fire(Events.java:116)
2021-12-27 17:51:45.525 E/DEBUG:     at com.paypal.pyplcheckout.services.callbacks.BaseCallback.handleApiSuccess(BaseCallback.kt:114)
2021-12-27 17:51:45.523 E/DEBUG:     at com.paypal.pyplcheckout.services.callbacks.ClientConfigUpdateCallback.onApiSuccess(ClientConfigUpdateCallback.kt:54)
2021-12-27 17:51:45.526 E/DEBUG:     at com.paypal.pyplcheckout.services.callbacks.BaseCallback.onResponse(BaseCallback.kt:68)
2021-12-27 17:51:45.525 E/DEBUG:     at com.paypal.pyplcheckout.services.callbacks.BaseCallback.handleApiSuccess(BaseCallback.kt:114)
2021-12-27 17:51:45.527 V/nxoBaseCallback: https://www.paypal.com/xoplatform/logger/api/logger returned with response
2021-12-27 17:51:45.526 E/DEBUG:     at com.paypal.pyplcheckout.services.callbacks.BaseCallback.onResponse(BaseCallback.kt:68)
2021-12-27 17:51:45.527 E/DEBUG:     at okhttp3.internal.connection.RealCall$AsyncCall.run(RealCall.kt:519)
2021-12-27 17:51:45.527 V/nxoBaseCallback: https://www.paypal.com/xoplatform/logger/api/logger returned with response
2021-12-27 17:51:45.529 E/DEBUG:     at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
2021-12-27 17:51:45.527 E/DEBUG:     at okhttp3.internal.connection.RealCall$AsyncCall.run(RealCall.kt:519)
2021-12-27 17:51:45.530 E/DEBUG:     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
2021-12-27 17:51:45.529 E/DEBUG:     at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
2021-12-27 17:51:45.531 E/DEBUG:     at java.lang.Thread.run(Thread.java:784)
复制代码
  • 5.以上操作Server-side integration集成就完成了,我再补上个参考TimberBug老哥和Paypal的Demo代码的Programmatically start the SDK客户端集成方式的测试代码以做备份:
  fun startPaypal(context: Activity){if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {PayPalCheckout.start(createOrder = CreateOrder { actions ->val createdItems = listOf(CreatedItem("TestGoods", "1", "1", "0.1", ItemCategory.DIGITAL_GOODS))//参数依次:商品名称,数量,单价,税费,商品类型val shippingPreference = ShippingPreference.NO_SHIPPING //航运信息,非必须val currencyCode = CurrencyCode.USD //收款货币Code//以下为统一计算总数,避免出现不一致情况val itemTotal = createdItems.map { it.amount.toDouble() * it.quantity.toInt() }.sum().toBigDecimal().scaledForMoneyval taxTotal = createdItems.map { it.taxAmount.toDouble() * it.quantity.toInt() }.sum().toBigDecimal().scaledForMoneyval shippingTotal = BigDecimal(0.00).scaledForMoneyval handlingTotal = BigDecimal(0.00).scaledForMoneyval shippingDiscountTotal = BigDecimal(0.00).scaledForMoneyval itemDiscountTotal = BigDecimal(0.00).scaledForMoneyval totalValue = itemTotal.add(taxTotal).add(shippingTotal).add(handlingTotal).subtract(shippingDiscountTotal).subtract(itemDiscountTotal)actions.create(Order.Builder().intent(OrderIntent.CAPTURE).purchaseUnitList(listOf(PurchaseUnit.Builder().referenceId(UUID.randomUUID().toString())//唯一id.amount(Amount.Builder().currencyCode(currencyCode).value(totalValue.asMoneyString).breakdown(BreakDown.Builder().itemTotal(itemTotal.unitAmountFor(currencyCode)).shipping(shippingTotal.unitAmountFor(currencyCode)).handling(handlingTotal.unitAmountFor(currencyCode)).taxTotal(taxTotal.unitAmountFor(currencyCode)).shippingDiscount(shippingDiscountTotal.unitAmountFor(currencyCode)).discount(itemDiscountTotal.unitAmountFor(currencyCode)).build()).build()).items(createdItems.map { createdItem ->Items.Builder().name(createdItem.name).quantity(createdItem.quantity).category(createdItem.itemCategory).unitAmount(UnitAmount.Builder().value(createdItem.amount).currencyCode(currencyCode).build()).tax(UnitAmount.Builder().value(createdItem.taxAmount).currencyCode(currencyCode).build()).build()}).shipping(Shipping.Builder().address(Address.Builder().addressLine1("123 Townsend St").addressLine2("Floor 6").adminArea2("San Francisco").adminArea1("CA").postalCode("94107").countryCode("US").build()).options(null).build())//航运地址,非必须,Omitting shipping will default to the customer's default shipping address..customId("CUSTOM-123")//The API caller-provided external ID. Used to reconcile API caller-initiated transactions with PayPal transactions. Appears in transaction and settlement reports..description("Purchase from Orders Quick Start").softDescriptor("800-123-1234")//The soft descriptor is the dynamic text used to construct the statement descriptor that appears on a payer's card statement.不太清楚有啥用.build() )).appContext(AppContext.Builder().brandName("公司品牌,非必须").userAction(UserAction.PAY_NOW).shippingPreference(shippingPreference).build()).build()){ id ->System.out.println("生成的订单ID: $id")}},onApprove = OnApprove { approval ->approval.orderActions.capture { result ->val message = when (result) {is CaptureOrderResult.Success -> {"Order Capture Succeeded"}is CaptureOrderResult.Error -> {"Order Capture Failed"}}System.out.println(message)}},onCancel = OnCancel {System.out.println("Buyer Cancelled Checkout")},onError = OnError { errorInfo ->System.out.println("An Error Occurred")})}else{System.out.println("payPal支付失败,系统版本过低")}}private fun BigDecimal.unitAmountFor(currencyCode: CurrencyCode): UnitAmount {return UnitAmount.Builder().value(asMoneyString).currencyCode(currencyCode).build()}private val BigDecimal.asMoneyString: Stringget() = DecimalFormat("#0.00").format(this)private val BigDecimal.scaledForMoney: BigDecimalget() = setScale(2, RoundingMode.HALF_UP)

Paypal集成准备工作相关推荐

  1. PayPal集成标准版案例(asp.net)关键源码

    Paypal国际版网站集成简易教程(一):序言 本文介绍如何在自己网站上集成paypal标准版支付,是从百度文库中找到的,要感谢下这位作者把自己的经验心得分享出来,毕竟paypal集成中文的教程非常少 ...

  2. Spring Boot入门教程(三十五):支付宝集成-准备工作

    分享一个朋友的人工智能教程.比较通俗易懂,风趣幽默,感兴趣的朋友可以去看看. 一:注册账号&创建应用 集成第三方的功能(无论是集成支付宝还是微信或者其他的都是一样)的第一步一般都是要跑到其类似 ...

  3. paypal集成国际支付

    网上有很多帖子,但感觉都是一样的,用完大多都是有问题的,我奋战几日,将paypal支付进行了整合以及整理,关于网页支付和APP端支付,都可以适用的 http://note.youdao.com/not ...

  4. 苍蝇眼睛_桌子的布局像苍蝇一样下降=)

    苍蝇眼睛 Ben Charlton, after a long and valiant battle with CSS has gone tableless. Yay! 本·查尔顿 ( Ben Cha ...

  5. (asp.net)PayPal案例的关键源码代码__PayPal集成_API接口

    轻轻松松集成PayPal 标准版 Paypal国际版网站集成简易教程(一):序言 前段时间接到一个外贸网站的项目,集成paypal支付接口的,对象是海外用户,全部用US的信用卡结算.国内有个paypa ...

  6. Android集成PayPal

    一,什么是PayPal? PayPal是eBay旗下的一家公司,致力于让个人或企业通过电子邮件,安全.简单.便捷地实现在线付款和收款.PayPal账户是PayPal公司推出的最安全的网络电子账户,使用 ...

  7. Paypal国际版网站集成

    前段时间接到一个外贸网站的项目,集成paypal支付接口的,对象是海外用户,全部用US的信用卡结算.所以只能使用paypal国际版,还好paypal上的资料非常齐全,而 且还提供了开发者沙盒(sand ...

  8. java paypal 支付集成_PayPal支付与动态Paypal帐户集成

    我有一个网站(内置Laravel),允许拥有PayPal帐户的商家出售他们的商品,这类似于没有购物车功能的eBay . 但是,我在将PayPal集成到我的网站时遇到了问题,因为我不知道确保数据正确的最 ...

  9. java paypal 支付集成_java – PayPal SDK从付款审核页面到个人资料页面

    在我目前的Java / Spring项目中,我正处于与PayPal集成的阶段.在配置Java类来处理付款流程后,按照here的说明,我运行我的应用程序并尝试使用paypal签出订单. 我被正确地重定向 ...

最新文章

  1. 深入剖析Redis系列(七) - Redis数据结构之列表
  2. leetcode算法题--出界的路径数★
  3. C++ 中const的用法,特别是用在函数前面与后面的区别!
  4. Linux基础练习题(三)
  5. OpenCASCADE:Modeling Algorithms模块之布尔运算
  6. 文献记录(part14)--Biclustering with dominant sets
  7. jQuery框架学习第四天:使用jQuery操作元素的属性与样式
  8. MapReduce之OutputFormat理解
  9. scp传服务器文件,使用tar、scp和screen在服务器之间传输文件
  10. 银监局计算机英语面试题及答案,2012年国家公务员银监局面试真题及参考答案...
  11. react前端封装接口弹出错误_react+ts打包发布后报Minified React error ..这种错误
  12. python运维平台开发_python运维平台的设计
  13. OPNsense用户手册-反病毒引擎
  14. 基于WinPcap的网络流量在线分析系统的设计与实现
  15. win7旗舰版系统下载
  16. 并发和并行的区别(图解)
  17. 在Centos7下源码安装配置Redis
  18. 5GHz超频教程:技嘉Z390 AORUS MASTER主板+酷睿i9
  19. 检查软件下载是否完整 MD5 工具使用 ----- md5sum
  20. R语言常微分方程数值解海强作业

热门文章

  1. Bat脚本 - 详解
  2. 支付宝 app支付报错 ALIN10146
  3. 判断本机ip是电信还是网通
  4. 电商运营年度报告怎么做?看这篇文章就够了
  5. Oracle Primavera P6软件建立项目工序权重
  6. SpringBoot下的策略模式,消灭了大量的ifelse,真香!
  7. 张驰咨询:流程是一剂万能良药吗?
  8. Python十分钟制作属于你自己的个性logo
  9. 浪涌保护器的选型和参数解析
  10. C语言文件联系人管理碎片整理,深入理解C语言内存管理.docx