likes
comments
collection
share

境外支付订阅(Paypal)原生API总结 (已上线)

作者站长头像
站长
· 阅读数 25

最近这周业务不忙了, 花点时间把前段时间干的事总结一下吧, 老师我太想进步了 🍓.

1. 背景/历程/收获/展望

  • 时间线: Q1季度中吧2月底了,具体时间忘了。

  • 背景: 在Q1季度最近webtoapp业务上要集成PayPal订阅/支付,主要用户购买订阅类型的商品,下发我们App会员的权益,其实在webtoapp端我们已经有Stripe stripe.com/ 信用卡支付了,结果ab实验整体跑下来的效果/数据是不好的,为了优化流程,去抄topx的产品形态,产品看到一年前以前的版本有papal支付现在又要加回来,业务需求是: 免费试用X天/周付/季付类型,进行续费 用户退订,过期的操作进行回调地址监听。

  • 历程:

    • Paypal操作流程特别麻烦,
      • 第一次接手什么都看不到,没办法 直接去看官方文档,找相关文档和相关接口阅读理解,Paypal后台开发不能进行登录,没有权限(风控严重)。
      • 我这边主要是用Python脚本进行调用Paypal官网提供的Api,根据自己需求进行创建对应规则的ProductId,再调用他们提供的订阅接口,Paypal返回一个购买的url给你,对刚刚创建的ProductId订阅,你再用沙盒的Paypal账号进行购买,订阅成功的话,你可以再paypal账户后台看到相关订单信息通过transactionId。
    • 中间遇到了很多坑,
      • 续费只会回调给我事件类型是 PAYMENT.SALE.COMPLETED 我还以为回调的是BILLING.SUBSCRIPTION.UPDATED (第一版本的回调使用的是这个,说明这个bug已经存在1年多了),没办法 还是官网找修改webhook内容的接口,添加新的类型在回调接口上面,沙盒环境和线上环境都调用一遍,这才解决问题。
      • 还有它上面写的下次扣费时间,其实是不准确的,上面写的下午6点扣费,等到它到了晚上9点才进行扣费,没办法不可能让用户扣了钱 结果还不是会员,先送一天会员处理下这个场景吧(都不知道以前怎么测试通过的,可能没什么用户量吧,没人反馈这些问题)。
      • 还有一个危险操作,购买是放在前端流程,前端代码里面存储了key/secret直接进行plan的购买,购买完成之后调用服务器接口进购买订单校验并且下发会员权益。
    • 费时费力(对我来说是有成长的),在一月份上线之后这个功能上线了3个月后,加上广告投入,4月份看了一次数据,好像有一个人购买了,不太记得是通过paypal支付还是stripe支付,这些都不太重要(已经换项目组了), 唉 最近一年做的东西一点成就感都没有,做一个功能垮一个,数据一直很差, 自己缺少产品思维,把自己的角色定义的太死了, 给不了产品好的意见。
  • 收获:

    • 开源自己封装好的脚本,由于Paypal账号风控非常严重,生产环境web页面操作容易封禁, 研发没有权限操作,我这边测试和线上都是使用Api创建Plan/Subscription/webhook 进行对接调用实现。主要总结Paypal-Api总结 订阅/Plan/webhook python3实现 github.com/hakusai22/P…
    • 快速阅读/理解文档/解决问题的能力有所提升,而不是以前那样有问题就找leader, 他们也不清楚这个业务啊,他们帮不了你 又不是出架构方案,只能靠自己去翻文档解决问题。
    • 对于支付订单这块的丢单逻辑没有考虑/没有写(失败就失败 用户自己重试/回调异常看paypal会不会重试),线上真实用户量太少了,给不了你发挥的空间。这种东西写一次就没意思(刚开始还觉得挺有趣的),全是没有用户量的架子,效果不好 说不定下个版本就换别的了。
  • 未来展望: 支付架构设计(越抽象越好😁),支付异常处理方案,配置和获取支付地址逻辑放在服务器端(安全)。

github.com/hakusai22/P… 脚本

  • 拿到沙盒/线上环境 client.id/secret/url
  • 沙盒账号周付/季付 免费试用3天 python3脚本创建。
  • Paypal 免费试用/直接付费逻辑添加
  • Paypal 后台回调/paypal/webhook 接口设置的是三个数据中心

境外支付订阅(Paypal)原生API总结 (已上线)

境外支付订阅(Paypal)原生API总结 (已上线)

1. Paypal 订阅API文档

官方文档

境外支付订阅(Paypal)原生API总结 (已上线)

一个产品ID 用户免费试用3天后 再进行周付的扣款

境外支付订阅(Paypal)原生API总结 (已上线)

下面都是一些接口调用的记录和使用模版。

1.1. 创建plan

1.1.1. 接口调用鉴权 header

if __name__ == '__main__':

    def get_http_headers():
        # 设置请求头
        headers = {
            "Content-Type": "application/json"
        }
        authorization = "xxxx-xx:xxx-xx-xxx"
        auth_encode = base64.b64encode(authorization.encode()).decode()
        final_auth = f"Basic {auth_encode}"
        headers["Authorization"] = final_auth
        return headers

1.1.1.1. 详细解析创建订阅body里面每个字段含义

{
	"product_id": "PROD-XXCD1234QWER65782", //sku id
	"name": "Video Streaming Service Plan", //name
	"description": "Video Streaming Service basic plan", //desc
	"status": "ACTIVE", //状态 CREATED/INACTIVE/ACTIVE
	//订单周期
	"billing_cycles": [
		{
			"frequency": {
				"interval_unit": "MONTH", //月 DAY/WEEK/MONTH/YEAR
				"interval_count": 1 // 1个月计费一次/
			},
			"tenure_type": "TRIAL", // 试用订阅 TRIAL/REGULAR
			"sequence": 1, //顺序
			"total_cycles": 2, //这个计费执行的次数
			"pricing_scheme": {
				"fixed_price": {
					"value": "3", //金额
					"currency_code": "USD" //货币单位
				}
			}
		},
		{
			"frequency": {
				"interval_unit": "MONTH",
				"interval_count": 1
			},
			"tenure_type": "TRIAL", // 试用订阅
			"sequence": 2, //顺序
			"total_cycles": 3,
			"pricing_scheme": {
				"fixed_price": {
					"value": "6",
					"currency_code": "USD"
				}
			}
		},
		{
			"frequency": {
				"interval_unit": "MONTH",
				"interval_count": 1
			},
			"tenure_type": "REGULAR", //常规订阅
			"sequence": 3, //顺序
			"total_cycles": 12,
			"pricing_scheme": {
				"fixed_price": {
					"value": "10",
					"currency_code": "USD"
				}
			}
		}
	],
	"payment_preferences": {
		"auto_bill_outstanding": true, //是否自动扣费
		//服务初始费用
		"setup_fee": {
			"value": "10",
			"currency_code": "USD"
		},
		"setup_fee_failure_action": "CONTINUE", //如果设置的初始付款失败,则继续订阅
		"payment_failure_threshold": 3 // 暂停订阅前的最大付款失败次数
	},
	"taxes": {
		"percentage": "10", //税的百分比
		"inclusive": false // 指示该税是否已包含在开单金额中
	}
}

1.2. 批量获取plan列表

import requests

headers = {
    'Authorization': 'Bearer A21AAGHr9qtiRRXH4oYcQokQgV99rGqEIfgrr8xHCclP0OzmD9KVgg5ppIIg1jzJgQkV4wd02svIvBJyg6cLFJjFow_SjBhxQ',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
    'Prefer': 'return=representation',
}

params = (
    ('sort_by', 'create_time'),
    ('sort_order', 'desc'),
)

response = requests.get('https://api-m.sandbox.paypal.com/v1/billing/plans', headers=headers, params=params)

请求数据

境外支付订阅(Paypal)原生API总结 (已上线)

{
	"plans": [
		{
			"id": "P-9CT60829WM695623HL7QGYOI", //paypal 生成plan的唯一id
			"name": "Netflix Plan 17012019", //name
			"status": "ACTIVE", //plan的状态 CREATED/INACTIVE/ACTIVE
			"description": "Netflix basic plan", 
			"usage_type": "LICENSED",
			"create_time": "2020-12-23T07:08:40Z", //创建时间
			"links": [
				{
					"href": "https://api-m.paypal.com/v1/billing/plans/P-9CT60829WM695623HL7QGYOI",
					"rel": "self",
					"method": "GET"
				}
			]
		},
		{
			"id": "P-7CE83846EJ264184CL7QHL3I",
			"name": "Netflix Plan 17012019",
			"status": "CREATED",
			"description": "Netflix basic plan",
			"usage_type": "LICENSED",
			"create_time": "2020-12-23T07:06:08Z",
			"links": [
				{
					"href": "https://api-m.paypal.com/v1/billing/plans/P-7CE83846EJ264184CL7QHL3I",
					"rel": "self",
					"method": "GET"
				}
			]
		}
	],
	"links": [
		{
			"href": "https://api-m.paypal.com/v1/billing/plans?page_size=10&page=1",
			"rel": "self",
			"method": "GET"
		}
	]
}

1.3. 根据id查出具体plan的信息

import requests

headers = {
	'X-PAYPAL-SECURITY-CONTEXT': '{"consumer":{"accountNumber":1181198218909172527,"merchantId":"5KW8F2FXKX5HA"},"merchant":{"accountNumber":1659371090107732880,"merchantId":"2J6QB8YJQSJRJ"},"apiCaller":{"clientId":"AdtlNBDhgmQWi2xk6edqJVKklPFyDWxtyKuXuyVT-OgdnnKpAVsbKHgvqHHP","appId":"APP-6DV794347V142302B","payerId":"2J6QB8YJQSJRJ","accountNumber":"1659371090107732880"},"scopes":["https://api-m.paypal.com/v1/subscription/.*","https://uri.paypal.com/services/subscription","openid"]}',
	'Content-Type': 'application/json',
	'Accept': 'application/json',
}

response = requests.get('https://api-m.sandbox.paypal.com/v1/billing/plans/P-5ML4271244454362WXNWU5NQ', headers=headers)

具体plan的信息 信息已脱敏


{
	"id": "xxxxxQ",
	"product_id": "xxxxx2",
	"name": "Basic Plan",
	"description": "Basic Plan",
	"status": "ACTIVE",
	"billing_cycles": [
		{
			"frequency": {
				"interval_unit": "MONTH",
				"interval_count": 1
			},
			"tenure_type": "TRIAL",
			"sequence": 1,
			"total_cycles": 2,
			// 价格
			"pricing_scheme": {
				"fixed_price": {
					"currency_code": "USD",
					"value": "3"
				},
				"version": 1,
				"create_time": "2020-05-27T12:13:51Z",
				"update_time": "2020-05-27T12:13:51Z"
			}
		},
		{
			"frequency": {
				"interval_unit": "MONTH",
				"interval_count": 1
			},
			"tenure_type": "TRIAL",
			"sequence": 2,
			"total_cycles": 3,
			"pricing_scheme": {
				"fixed_price": {
					"currency_code": "USD",
					"value": "6"
				},
				"version": 1,
				"create_time": "2020-05-27T12:13:51Z",
				"update_time": "2020-05-27T12:13:51Z"
			}
		},
		{
			"frequency": {
				"interval_unit": "MONTH",
				"interval_count": 1
			},
			"tenure_type": "REGULAR",
			"sequence": 3,
			"total_cycles": 12,
			"pricing_scheme": {
				"fixed_price": {
					"value": "10",
					"currency_code": "USD"
				},
				"status": "ACTIVE",
				"version": 1,
				"create_time": "2020-05-27T12:13:51Z",
				"update_time": "2020-05-27T12:13:51Z"
			}
		}
	],
	"taxes": {
		"percentage": "10",
		"inclusive": false
	},
	"create_time": "2020-05-27T12:13:51Z",
	"update_time": "2020-05-27T12:13:51Z",
	"links": [
		{
			"href": "https://api-m.paypal.com/v1/billing/plans/P-zzz",
			"rel": "self",
			"method": "GET"
		},
		{
			"href": "https://api-m.paypal.com/v1/billing/plans/P-zzz",
			"rel": "edit",
			"method": "PATCH"
		},
		{
			"href": "https://api-m.paypal.com/v1/billing/plans/P-zzz/deactivate",
			"rel": "deactivate",
			"method": "POST"
		},
		{
			"href": "https://api-m.paypal.com/v1/billing/plans/P-zzz/update-pricing-schemes",
			"rel": "edit",
			"method": "POST"
		}
	]
}

1.4. 更新plan

import requests

headers = {
	'Authorization': 'Bearer xxxxx',
	'Content-Type': 'application/json',
	'Accept': 'application/json',
}

data = '[
    {
        "op": "replace",
        "path": "/payment_preferences/payment_failure_threshold",
        "value": 7
    },
    {
        "op": "replace",
        "path": "/name",
        "value": "Updated Video Streaming Service Plan"
    }
]'

response = requests.patch('https://api-m.sandbox.paypal.com/v1/billing/plans/P-xxx', headers=headers, data=data)

1.5. 激活plan

import requests

headers = {
    'X-PAYPAL-SECURITY-CONTEXT': '{"consumer":{"accountNumber":xx,"merchantId":"xxx"},"merchant":{"accountNumber":xxx,"merchantId":"xxx"},"apiCaller":{"clientId":"xx-xx","appId":"APP-xx","payerId":"xxx","accountNumber":"xx"},"scopes":["https://api-m.paypal.com/v1/subscription/.*","https://uri.paypal.com/services/subscription","openid"]}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
}

response = requests.post('https://api-m.sandbox.paypal.com/v1/billing/plans/P-7GL4271244454362WXNWU5NQ/activate', headers=headers)

1.6. 停用plan

import requests

headers = {
    'X-PAYPAL-SECURITY-CONTEXT': '{"consumer":{"accountNumber":xx,"merchantId":"5KW8F2FXKX5HA"},"merchant":{"accountNumber":1659371090107732880,"merchantId":"2J6QB8YJQSJRJ"},"apiCaller":{"clientId":"AdtlNBDhgmQWi2xk6edqJVKklPFyDWxtyKuXuyVT-OgdnnKpAVsbKHgvqHHP","appId":"APP-6DV794347V142302B","payerId":"2J6QB8YJQSJRJ","accountNumber":"1659371090107732880"},"scopes":["https://api-m.paypal.com/v1/subscription/.*","https://uri.paypal.com/services/subscription","openid"]}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
}

response = requests.post('https://api-m.sandbox.paypal.com/v1/billing/plans/P-7GL4271244454362WXNWU5NQ/deactivate', headers=headers)

1.7. 更新订阅价格

import requests

headers = {
    'X-PAYPAL-SECURITY-CONTEXT': '{"consumer":{"accountNumber":1181198218909172527,"merchantId":"5KW8F2FXKX5HA"},"merchant":{"accountNumber":1659371090107732880,"merchantId":"2J6QB8YJQSJRJ"},"apiCaller":{"clientId":"AdtlNBDhgmQWi2xk6edqJVKklPFyDWxtyKuXuyVT-OgdnnKpAVsbKHgvqHHP","appId":"APP-6DV794347V142302B","payerId":"2J6QB8YJQSJRJ","accountNumber":"1659371090107732880"},"scopes":["https://api-m.paypal.com/v1/subscription/.*","https://uri.paypal.com/services/subscription","openid"]}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
}

data = '{
    "plan_id": "P-5ML4271244454362WXNWU5NQ",
    "start_time": "2018-11-01T00:00:00Z",
    "quantity": "20",
    "shipping_amount": {
        "currency_code": "USD",
        "value": "10.00"
    },
    "subscriber": {
        "name": {
            "given_name": "John",
            "surname": "Doe"
        },
        "email_address": "customer@example.com",
        "shipping_address": {
            "name": {
                "full_name": "John Doe"
            },
            "address": {
                "address_line_1": "2211 N First Street",
                "address_line_2": "Building 17",
                "admin_area_2": "San Jose",
                "admin_area_1": "CA",
                "postal_code": "95131",
                "country_code": "US"
            }
        }
    },
    "application_context": {
        "brand_name": "walmart",
        "locale": "en-US",
        "shipping_preference": "SET_PROVIDED_ADDRESS",
        "user_action": "SUBSCRIBE_NOW",
        "payment_method": {
            "payer_selected": "PAYPAL",
            "payee_preferred": "IMMEDIATE_PAYMENT_REQUIRED"
        },
        "return_url": "https://example.com/returnUrl",
        "cancel_url": "https://example.com/cancelUrl"
    }
}'

response = requests.post('https://api-m.sandbox.paypal.com/v1/billing/plans/P-7GL4271244454362WXNWU5NQ/update-pricing-schemes', headers=headers, data=data)

1.8. 通过plan创建订阅

import requests

headers = {
    'Authorization': 'Bearer A21AAGHr9qtiRRXH4oYcQokQgV99rGqEIfgrr8xHCclP0OzmD9KVgg5ppIIg1jzJgQkV4wd02svIvBJyg6cLFJjFow_SjBhxQ',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
    'PayPal-Request-Id': 'SUBSCRIPTION-21092019-001',
    'Prefer': 'return=representation',
}

data = '{ "plan_id": "P-5ML4271244454362WXNWU5NQ", "start_time": "2018-11-01T00:00:00Z", "quantity": "20", "shipping_amount": { "currency_code": "USD", "value": "10.00" }, "subscriber": { "name": { "given_name": "John", "surname": "Doe" }, "email_address": "customer@example.com", "shipping_address": { "name": { "full_name": "John Doe" }, "address": { "address_line_1": "2211 N First Street", "address_line_2": "Building 17", "admin_area_2": "San Jose", "admin_area_1": "CA", "postal_code": "95131", "country_code": "US" } } }, "application_context": { "brand_name": "walmart", "locale": "en-US", "shipping_preference": "SET_PROVIDED_ADDRESS", "user_action": "SUBSCRIBE_NOW", "payment_method": { "payer_selected": "PAYPAL", "payee_preferred": "IMMEDIATE_PAYMENT_REQUIRED" }, "return_url": "https://example.com/returnUrl", "cancel_url": "https://example.com/cancelUrl" } }'

response = requests.post('https://api-m.sandbox.paypal.com/v1/billing/subscriptions', headers=headers, data=data)

1.9. 通过订阅id查询订阅详情

import requests

headers = {
    'X-PAYPAL-SECURITY-CONTEXT': '{"consumer":{"accountNumber":1181198218909172527,"merchantId":"5KW8F2FXKX5HA"},"merchant":{"accountNumber":1659371090107732880,"merchantId":"2J6QB8YJQSJRJ"},"apiCaller":{"clientId":"AdtlNBDhgmQWi2xk6edqJVKklPFyDWxtyKuXuyVT-OgdnnKpAVsbKHgvqHHP","appId":"APP-6DV794347V142302B","payerId":"2J6QB8YJQSJRJ","accountNumber":"1659371090107732880"},"scopes":["https://api-m.paypal.com/v1/subscription/.*","https://uri.paypal.com/services/subscription","openid"]}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
}

response = requests.get('https://api-m.sandbox.paypal.com/v1/billing/subscriptions/I-BW452GLLEP1G', headers=headers)

订阅详情 已脱敏

{
	"id": "I-xxx",
	"plan_id": "P-xx",
	"start_time": "2019-04-10T07:00:00Z",
	"quantity": "20",
	"shipping_amount": {
		"currency_code": "USD",
		"value": "10.0"
	},
	"subscriber": {
		"shipping_address": {
			"name": {
				"full_name": "John Doe"
			},
			"address": {
				"address_line_1": "2211 N First Street",
				"address_line_2": "Building 17",
				"admin_area_2": "San Jose",
				"admin_area_1": "CA",
				"postal_code": "95131",
				"country_code": "US"
			}
		},
		"name": {
			"given_name": "John",
			"surname": "Doe"
		},
		"email_address": "customer@example.com",
		"payer_id": "2J6QB8YJQSJRJ"
	},
	"billing_info": {
		"outstanding_balance": {
			"currency_code": "USD",
			"value": "1.0"
		},
		"cycle_executions": [
			{
				"tenure_type": "TRIAL",
				"sequence": 1,
				"cycles_completed": 0,
				"cycles_remaining": 2,
				"total_cycles": 2
			},
			{
				"tenure_type": "TRIAL",
				"sequence": 2,
				"cycles_completed": 0,
				"cycles_remaining": 3,
				"total_cycles": 3
			},
			{
				"tenure_type": "REGULAR",
				"sequence": 3,
				"cycles_completed": 0,
				"cycles_remaining": 12,
				"total_cycles": 12
			}
		],
		"last_payment": {
			"amount": {
				"currency_code": "USD",
				"value": "1.15"
			},
			"time": "2019-04-09T10:27:20Z"
		},
		"next_billing_time": "2019-04-10T10:00:00Z",
		"failed_payments_count": 0
	},
	"create_time": "2019-04-09T10:26:04Z",
	"update_time": "2019-04-09T10:27:27Z",
	"links": [
		{
			"href": "https://api-m.paypal.com/v1/billing/subscriptions/I-xx/cancel",
			"rel": "cancel",
			"method": "POST"
		},
		{
			"href": "https://api-m.paypal.com/v1/billing/subscriptions/I-xxx",
			"rel": "edit",
			"method": "PATCH"
		},
		{
			"href": "https://api-m.paypal.com/v1/billing/subscriptions/I-xxx",
			"rel": "self",
			"method": "GET"
		},
		{
			"href": "https://api-m.paypal.com/v1/billing/subscriptions/I-xxx/suspend",
			"rel": "suspend",
			"method": "POST"
		},
		{
			"href": "https://api-m.paypal.com/v1/billing/subscriptions/I-xxx/capture",
			"rel": "capture",
			"method": "POST"
		}
	],
	"status": "ACTIVE",
	"status_update_time": "2019-04-09T10:27:27Z"
}

1.10. 更新订阅

import requests

headers = {
	'Authorization': 'Bearer xxx',
	'Content-Type': 'application/json',
	'Accept': 'application/json',
}

data = {
	'[
    {
        "op": "replace",
        "path": "/plan/billing_cycles/@sequence': '=1/pricing_scheme/fixed_price",
        "value": {
            "currency_code": "USD",
            "value": "50.00"
        }
    },
    {
        "op": "replace",
        "path": "/plan/billing_cycles/@sequence==2/pricing_scheme/tiers",
        "value": [
            {
                "starting_quantity": "1",
                "ending_quantity": "1000",
                "amount": {
                    "value": "500",
                    "currency_code": "USD"
                }
            },
            {
                "starting_quantity": "1001",
                "amount": {
                    "value": "2000",
                    "currency_code": "USD"
                }
            }
        ]
    },
    {
        "op": "replace",
        "path": "/plan/payment_preferences/auto_bill_outstanding",
        "value": true
    },
    {
        "op": "replace",
        "path": "/plan/payment_preferences/payment_failure_threshold",
        "value": 1
    },
    {
        "op": "replace",
        "path": "/plan/taxes/percentage",
        "value": "10"
    }
]'
}

response = requests.patch('https://api-m.sandbox.paypal.com/v1/billing/subscriptions/I-BW452GLLEP1G', headers=headers, data=data)

1.11. 修改订阅plan或者数量

import requests

headers = {
    'Authorization': 'Bearer A21AAGHr9qtiRRXH4oYcQokQgV99rGqEIfgrr8xHCclP0OzmD9KVgg5ppIIg1jzJgQkV4wd02svIvBJyg6cLFJjFow_SjBhxQ',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
}

data = '{ "plan_id": "P-5ML4271244454362WXNWU5NQ", "shipping_amount": { "currency_code": "USD", "value": "10.00" }, "shipping_address": { "name": { "full_name": "John Doe" }, "address": { "address_line_1": "2211 N First Street", "address_line_2": "Building 17", "admin_area_2": "San Jose", "admin_area_1": "CA", "postal_code": "95131", "country_code": "US" } }, "application_context": { "brand_name": "walmart", "locale": "en-US", "shipping_preference": "SET_PROVIDED_ADDRESS", "payment_method": { "payer_selected": "PAYPAL", "payee_preferred": "IMMEDIATE_PAYMENT_REQUIRED" }, "return_url": "https://example.com/returnUrl", "cancel_url": "https://example.com/cancelUrl" } }'

response = requests.post('https://api-m.sandbox.paypal.com/v1/billing/subscriptions/I-BW452GLLEP1G/revise', headers=headers, data=data)

1.12. 暂停订阅

import requests

headers = {
    'X-PAYPAL-SECURITY-CONTEXT': '{"consumer":{"accountNumber":1181198218909172527,"merchantId":"5KW8F2FXKX5HA"},"merchant":{"accountNumber":1659371090107732880,"merchantId":"2J6QB8YJQSJRJ"},"apiCaller":{"clientId":"AdtlNBDhgmQWi2xk6edqJVKklPFyDWxtyKuXuyVT-OgdnnKpAVsbKHgvqHHP","appId":"APP-6DV794347V142302B","payerId":"2J6QB8YJQSJRJ","accountNumber":"1659371090107732880"},"scopes":["https://api-m.paypal.com/v1/subscription/.*","https://uri.paypal.com/services/subscription","openid"]}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
}

data = '{ "reason": "Item out of stock" }'

response = requests.post('https://api-m.sandbox.paypal.com/v1/billing/subscriptions/I-BW452GLLEP1G/suspend', headers=headers, data=data)

1.13. 取消订阅

import requests

headers = {
    'X-PAYPAL-SECURITY-CONTEXT': '{"consumer":{"accountNumber":1181198218909172527,"merchantId":"5KW8F2FXKX5HA"},"merchant":{"accountNumber":1659371090107732880,"merchantId":"2J6QB8YJQSJRJ"},"apiCaller":{"clientId":"AdtlNBDhgmQWi2xk6edqJVKklPFyDWxtyKuXuyVT-OgdnnKpAVsbKHgvqHHP","appId":"APP-6DV794347V142302B","payerId":"2J6QB8YJQSJRJ","accountNumber":"1659371090107732880"},"scopes":["https://api-m.paypal.com/v1/subscription/.*","https://uri.paypal.com/services/subscription","openid"]}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
}

data = '{ "reason": "Not satisfied with the service" }'

response = requests.post('https://api-m.sandbox.paypal.com/v1/billing/subscriptions/I-BW452GLLEP1G/cancel', headers=headers, data=data)

1.14. 激活订阅

import requests

headers = {
    'X-PAYPAL-SECURITY-CONTEXT': '{"consumer":{"accountNumber":1181198218909172527,"merchantId":"5KW8F2FXKX5HA"},"merchant":{"accountNumber":1659371090107732880,"merchantId":"2J6QB8YJQSJRJ"},"apiCaller":{"clientId":"AdtlNBDhgmQWi2xk6edqJVKklPFyDWxtyKuXuyVT-OgdnnKpAVsbKHgvqHHP","appId":"APP-6DV794347V142302B","payerId":"2J6QB8YJQSJRJ","accountNumber":"1659371090107732880"},"scopes":["https://api-m.paypal.com/v1/subscription/.*","https://uri.paypal.com/services/subscription","openid"]}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
}

data = '{ "reason": "Reactivating the subscription" }'

response = requests.post('https://api-m.sandbox.paypal.com/v1/billing/subscriptions/I-BW452GLLEP1G/activate', headers=headers, data=data)

1.15. 在订阅上捕获来自订阅者的授权付款

import requests

headers = {
    'X-PAYPAL-SECURITY-CONTEXT': '{"consumer":{"accountNumber":1181198218909172527,"merchantId":"5KW8F2FXKX5HA"},"merchant":{"accountNumber":1659371090107732880,"merchantId":"2J6QB8YJQSJRJ"},"apiCaller":{"clientId":"AdtlNBDhgmQWi2xk6edqJVKklPFyDWxtyKuXuyVT-OgdnnKpAVsbKHgvqHHP","appId":"APP-6DV794347V142302B","payerId":"2J6QB8YJQSJRJ","accountNumber":"1659371090107732880"},"scopes":["https://api-m.paypal.com/v1/subscription/.*","https://uri.paypal.com/services/subscription","openid"]}',
    'Authorization': 'Bearer access_token6V7rbVwmlM1gFZKW_8QtzWXqpcwQ6T5vhEGYNJDAAdn3paCgRpdeMdVYmWzgbKSsECednupJ3Zx5Xd-g',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
    'PayPal-Request-Id': 'CAPTURE-160919-A0051',
}

data = '{
    "note": "Charging as the balance reached the limit",
    "capture_type": "OUTSTANDING_BALANCE",
    "amount": {
        "currency_code": "USD",
        "value": "100"
    }
}'

response = requests.post('https://api-m.sandbox.paypal.com/v1/billing/subscriptions/I-BW452GLLEP1G/capture', headers=headers, data=data)

1.16. 通过订阅id查询出所有的交易列表 时间范围

import requests

headers = {
    'X-PAYPAL-SECURITY-CONTEXT': '{"consumer":{"accountNumber":1181198218909172527,"merchantId":"5KW8F2FXKX5HA"},"merchant":{"accountNumber":1659371090107732880,"merchantId":"2J6QB8YJQSJRJ"},"apiCaller":{"clientId":"AdtlNBDhgmQWi2xk6edqJVKklPFyDWxtyKuXuyVT-OgdnnKpAVsbKHgvqHHP","appId":"APP-6DV794347V142302B","payerId":"2J6QB8YJQSJRJ","accountNumber":"1659371090107732880"},"scopes":["https://api-m.paypal.com/v1/subscription/.*","https://uri.paypal.com/services/subscription","openid"]}',
    'Content-Type': 'application/json',
    'Accept': 'application/json',
}

params = (
    ('start_time', '2018-01-21T07:50:20.940Z'),
    ('end_time', '2018-08-21T07:50:20.940Z'),
)

response = requests.get('https://api-m.sandbox.paypal.com/v1/billing/subscriptions/I-BW452GLLEP1G/transactions', headers=headers, params=params)

#NB. Original query string below. It seems impossible to parse and
#reproduce query strings 100% accurately so the one below is given
#in case the reproduced version is not "correct".
# response = requests.get('https://api-m.sandbox.paypal.com/v1/billing/subscriptions/I-BW452GLLEP1G/transactions?start_time=2018-01-21T07:50:20.940Z&end_time=2018-08-21T07:50:20.940Z', headers=headers)
{
	"transactions": [
		{
			"id": "TRFGHNJKOIIOJKL",
			"status": "COMPLETED",
			"payer_email": "customer@example.com",
			"payer_name": {
				"given_name": "John",
				"surname": "Doe"
			},
			"amount_with_breakdown": {
				"gross_amount": {
					"currency_code": "USD",
					"value": "10.00"
				},
				"fee_amount": {
					"currency_code": "USD",
					"value": "1.00"
				},
				"net_amount": {
					"currency_code": "USD",
					"value": "9.00"
				}
			},
			"time": "2018-03-16T07:40:20.940Z"
		},
		{
			"id": "VDFG45345FFGS",
			"status": "COMPLETED",
			"payer_email": "customer2@example.com",
			"payer_name": {
				"given_name": "Jhonny",
				"surname": "Cat"
			},
			"amount_with_breakdown": {
				"gross_amount": {
					"currency_code": "USD",
					"value": "15.00"
				},
				"fee_amount": {
					"currency_code": "USD",
					"value": "1.00"
				},
				"net_amount": {
					"currency_code": "USD",
					"value": "14.00"
				}
			},
			"time": "2018-08-21T07:50:20.940Z"
		}
	],
	"links": [
		{
			"href": "https://api-m.paypal.com/v1/billing/subscriptions/I-BW452GLLEP1G/transactions?start_time=2018-01-21T07:50:20.940Z&end_time=2018-08-21T07:50:20.940Z",
			"rel": "self",
			"method": "GET"
		}
	]
}

1.17. WebHook (用户取消订阅/续费)

境外支付订阅(Paypal)原生API总结 (已上线)

1.17.1. 获取后台设置的回调接口列表

import base64
import json
import requests


# -*- coding: utf-8 -*-
# @Author  : hakusai
# @Time    : 2024/03/05 10:10

if __name__ == '__main__':

    def get_http_headers():
        # 设置请求头
        headers = {
            "Content-Type": "application/json"
        }
        authorization = "xxx-xxxx:xxx-xxx-xxx"
        auth_encode = base64.b64encode(authorization.encode()).decode()
        final_auth = f"Basic {auth_encode}"
        headers["Authorization"] = final_auth
        return headers


    response = requests.get('https://api-m.sandbox.paypal.com/v1/notifications/webhooks', headers=get_http_headers())
    print(response.status_code)
    print(response.text)

回调Body数据

{
	"id": "8PT597110X687430LKGECATA",
	"create_time": "2013-06-25T21:41:28Z",
	"resource_type": "authorization",
	"event_type": "PAYMENT.AUTHORIZATION.CREATED", //事件类型
	"summary": "A payment authorization was created",//回调说明
	"resource": {
		"id": "2DC87612EK520411B", //订单id
		"create_time": "2013-06-25T21:39:15Z",
		"update_time": "2013-06-25T21:39:17Z",
		"state": "authorized",
		"amount": {
			"total": "7.47",
			"currency": "USD",
			"details": {
				"subtotal": "7.47"
			}
		},
		"parent_payment": "PAY-36246664YD343335CKHFA4AY",
		"valid_until": "2013-07-24T21:39:15Z",
		"links": [
			{
				"href": "https://api-m.sandbox.paypal.com/v1/payments/authorization/2DC87612EK520411B",
				"rel": "self",
				"method": "GET"
			},
			{
				"href": "https://api-m.sandbox.paypal.com/v1/payments/authorization/2DC87612EK520411B/capture",
				"rel": "capture",
				"method": "POST"
			},
			{
				"href": "https://api-m.sandbox.paypal.com/v1/payments/authorization/2DC87612EK520411B/void",
				"rel": "void",
				"method": "POST"
			},
			{
				"href": "https://api-m.sandbox.paypal.com/v1/payments/payment/PAY-36246664YD343335CKHFA4AY",
				"rel": "parent_payment",
				"method": "GET"
			}
		]
	},
	"links": [
		{
			"href": "https://api-m.sandbox.paypal.com/v1/notifications/webhooks-events/8PT597110X687430LKGECATA",
			"rel": "self",
			"method": "GET"
		},
		{
			"href": "https://api-m.sandbox.paypal.com/v1/notifications/webhooks-events/8PT597110X687430LKGECATA/resend",
			"rel": "resend",
			"method": "POST"
		}
	]
}

首次订阅成功会回调两个事件:

1.17.2. BILLING.SUBSCRIPTION.ACTIVATED

{
    "id": "WH-2JK13221SH228131A-85M888883CF992091V",
    "event_version": "1.0",
    "create_time": "2022-08-02T09:58:37.896Z",
    "resource_type": "subscription",
    "resource_version": "2.0",
    "event_type": "BILLING.SUBSCRIPTION.ACTIVATED",
    "summary": "Subscription activated",
    "resource": {
        "quantity": "1",
        "subscriber": {
            "email_address": "wwww@personal.example.com",
            "payer_id": "BNMR2YZQJ9Q2L",
            "name": {
                "given_name": "John",
                "surname": "Doe"
            }
        },
        "create_time": "2022-08-02T09:58:05Z",
        "plan_overridden": false,
        "shipping_amount": {
            "currency_code": "USD",
            "value": "0.0"
        },
        "start_time": "2022-08-02T09:57:17Z",
        "update_time": "2022-08-02T09:58:06Z",
        "billing_info": {
            "outstanding_balance": {
                "currency_code": "USD",
                "value": "0.0"
            },
            "cycle_executions": [
                {
                    "tenure_type": "TRIAL",
                    "sequence": 1,
                    "cycles_completed": 1,
                    "cycles_remaining": 0,
                    "current_pricing_scheme_version": 1,
                    "total_cycles": 1
                },
                {
                    "tenure_type": "REGULAR",
                    "sequence": 2,
                    "cycles_completed": 0,
                    "cycles_remaining": 0,
                    "current_pricing_scheme_version": 1,
                    "total_cycles": 0
                }
            ],
            "last_payment": {
                "amount": {
                    "currency_code": "USD",
                    "value": "0.99"
                },
                "time": "2022-08-02T09:58:05Z"
            },
            "next_billing_time": "2022-08-03T10:00:00Z",
            "failed_payments_count": 0
        },
        "links": [
            {
                "href": "https://api.sandbox.paypal.com/v1/billing/subscriptions/I-DL5L53KM3N/cancel",
                "rel": "cancel",
                "method": "POST",
                "encType": "application/json"
            },
            {
                "href": "https://api.sandbox.paypal.com/v1/billing/subscriptions/I-DL5L53KM3N",
                "rel": "edit",
                "method": "PATCH",
                "encType": "application/json"
            },
            {
                "href": "https://api.sandbox.paypal.com/v1/billing/subscriptions/I-DL5L53KM3N",
                "rel": "self",
                "method": "GET",
                "encType": "application/json"
            },
            {
                "href": "https://api.sandbox.paypal.com/v1/billing/subscriptions/I-DL5L53KM3N/suspend",
                "rel": "suspend",
                "method": "POST",
                "encType": "application/json"
            },
            {
                "href": "https://api.sandbox.paypal.com/v1/billing/subscriptions/I-DL5L53KM3N/capture",
                "rel": "capture",
                "method": "POST",
                "encType": "application/json"
            }
        ],
        "id": "I-DL5L53KM3N",
        "plan_id": "P-4G238388SD824MLUPJNY",
        "status": "ACTIVE",
        "status_update_time": "2022-08-02T09:58:06Z"
    },
    "links": [
        {
            "href": "https://api.sandbox.paypal.com/v1/notifications/webhooks-events/WH-2JK13221SH228131A-85M80963CF992091V",
            "rel": "self",
            "method": "GET"
        },
        {
            "href": "https://api.sandbox.paypal.com/v1/notifications/webhooks-events/WH-2JK13221SH228131A-85M80963CF992091V/resend",
            "rel": "resend",
            "method": "POST"
        }
    ]
}

1.17.3. 续订时只会回调:PAYMENT.SALE.COMPLETED

续费只会回调给我事件=PAYMENT.SALE.COMPLETED 我还以为回调的是BILLING.SUBSCRIPTION.UPDATED

{
    "id": "WH-4AU27166UL181633V-3BU78225VU943751T",
    "event_version": "1.0",
    "create_time": "2022-08-02T09:58:43.670Z",
    "resource_type": "sale",
    "event_type": "PAYMENT.SALE.COMPLETED",
    "summary": "Payment completed for $ 0.99 USD",
    "resource": {
        "billing_agreement_id": "I-DL5L53KM3N",
        "amount": {
            "total": "0.99",
            "currency": "USD",
            "details": {
                "subtotal": "0.99"
            }
        },
        "payment_mode": "INSTANT_TRANSFER",
        "update_time": "2022-08-02T09:58:05Z",
        "create_time": "2022-08-02T09:58:05Z",
        "protection_eligibility_type": "ITEM_NOT_RECEIVED_ELIGIBLE,UNAUTHORIZED_PAYMENT_ELIGIBLE",
        "transaction_fee": {
            "currency": "USD",
            "value": "0.33"
        },
        "protection_eligibility": "ELIGIBLE",
        "links": [
            {
                "method": "GET",
                "rel": "self",
                "href": "https://api.sandbox.paypal.com/v1/payments/sale/4DN9460xxx61205Y"
            },
            {
                "method": "POST",
                "rel": "refund",
                "href": "https://api.sandbox.paypal.com/v1/payments/sale/4DN9460xxx61205Y/refund"
            }
        ],
        "id": "4DN9460xxx61205Y",
        "state": "completed",
        "invoice_number": ""
    },
    "links": [
        {
            "href": "https://api.sandbox.paypal.com/v1/notifications/webhooks-events/WH-4AU27166UL181633V-3BU78225VU943751T",
            "rel": "self",
            "method": "GET"
        },
        {
            "href": "https://api.sandbox.paypal.com/v1/notifications/webhooks-events/WH-4AU27166UL181633V-3BU78225VU943751T/resend",
            "rel": "resend",
            "method": "POST"
        }
    ]
}

2. PayPal SDK 接入文档 (废弃)

  • Java 集成SDK 我们项目在2022年之前就集成了SDK, 后面paypal官方不进行维护,我们项目里面都是使用原生Api 自己写鉴权逻辑使用webclient进行对接,既然SDK都过时了,我就不进行文档总结了。
<dependency>
	<groupId>com.paypal.sdk</groupId>
	<artifactId>checkout-sdk</artifactId>
	<version>${paypal.sdk.version}</version>
</dependency>

境外支付订阅(Paypal)原生API总结 (已上线)

最后写完了 10点半了 陈伯直播了, 打麻将去了 八木唯

境外支付订阅(Paypal)原生API总结 (已上线)

境外支付订阅(Paypal)原生API总结 (已上线)

3. 参考

转载自:https://juejin.cn/post/7373529708262981642
评论
请登录