快速开始使用Stripe
简介
Stripe是一个支付处理平台。用例包括:
- 货币化访问昂贵的API调用
- 货币化高级扩展功能
- 销售主题、商品、实体和数字商品等
场景
你是一家SaaS公司,希望通过扩展向客户提供高级API服务。你希望用户在扩展可以访问此高级功能之前每月支付5美元。
设置Stripe产品链接
由于Manifest v3对远程代码执行的限制 (opens in a new tab),将符合PCI标准的支付系统集成到扩展中的选项有限。最简单的方法是设置一个Stripe产品链接。
要设置Stripe产品链接,必须创建一个Stripe产品。前往Stripe产品仪表板 (opens in a new tab)页面,然后点击添加产品,并填写信息:
然后,进入产品页面并点击“创建支付链接”按钮:
上述步骤应能获取到Stripe支付链接。对于后端授权,请前往Stripe仪表板主页 (opens in a new tab)获取密钥:
使用环境变量
假设你已经设置了一个基本的Plasmo项目,首先要做的就是设置我们的环境变量:
PLASMO_PUBLIC_STRIPE_LINK=https://buy.stripe.com/test_XXXXXXXX
STRIPE_PRIVATE_API_KEY=sk_test_xxxxxxxxxxxxxxxxxxxxxx
为了启用TypeScript智能感知,创建一个index.d.ts
文件:
.index.d.ts
(opens in a new tab)
访问Chrome身份API
为了将订阅与用户关联,我们可以使用他们的电子邮件地址。一种快速的方法是利用Chrome扩展的身份API (opens in a new tab)。为防止未经授权的访问,我们需要设置OAuth2认证模式,其工作原理如下:
- 我们的扩展生成OAuth2访问令牌
- 扩展发送带有令牌的请求到我们的后端
- 后端验证令牌以获取用户的电子邮件地址
- 后端查询用户的订阅状态
要启用此功能所需的权限,请将以下内容添加到你的package.json
文件的manifest
字段中:
{
...
"manifest": {
...
"permissions": ["identity", "identity.email"]
}
}
...
表示如果你已经有任何内容,请保留它。你会在我们的许多代码示例中看到这一点。
然后,我们需要使用Google Cloud Platform(GCP)设置OAuth2客户端ID。按照本指南 (opens in a new tab)快速创建一个新的GCP项目,然后导航到凭据页面:https://console.cloud.google.com/apis/credentials?referrer=search&project=<YOUR_PROJECT_ID>
。它会显示如下内容:
点击“创建凭据”,然后选择“OAuth客户端ID”:
在下一页,选择“Chrome应用”。表单将要求输入“应用程序ID”:
这将是你的扩展ID - 下一节将介绍如何获取它。
为开发设置固定的扩展ID
你会希望为开发固定你的扩展ID。如果你不小心从浏览器中删除了开发扩展,扩展ID将会丢失,并且你的OAuth2客户端将失效。
由于Chromium从公钥派生扩展ID,你可以通过生成自己的key
来固定它。你可以在package.json
的manifest覆盖中指定它。我们可以通过遵循这个Stack Overflow回答 (opens in a new tab)来生成此密钥:
- 生成私钥:
openssl genrsa 2048 | openssl pkcs8 -topk8 -nocrypt -out key.pem
- 从上面的私钥生成公钥:
openssl rsa -in key.pem -pubout -outform DER | openssl base64 -A
我们可以通过利用manifest覆盖中的环境变量来使用此密钥:
...
CRX_PUBLIC_KEY=v47xxx
{
"manifest": {
...
"key": "$CRX_PUBLIC_KEY"
}
}
运行开发服务器,然后将扩展加载到浏览器中。然后复制扩展ID:
将ID粘贴到OAuth表单的应用程序ID字段中,然后提交。你将收到OAuth2客户端ID:
将其添加到你的环境变量中:
...
OAUTH_CLIENT_ID=<YOUR_OAUTH_CLIENT_ID>
并在我们的manifest覆盖中使用它:
{
...
"manifest": {
...
"oauth2": {
"client_id": "$OAUTH_CLIENT_ID",
"scopes": [
"https://www.googleapis.com/auth/userinfo.email",
"https://www.googleapis.com/auth/userinfo.profile"
]
}
}
}
我们现在准备好生成OAuth访问令牌以授权和处理用户的订阅!
获取用户信息
我们可以使用chrome.identity.getProfileUserInfo
来了解用户是谁。为了缓存这些数据并在整个应用中重用,我们可以创建一个快速的React上下文 (opens in a new tab)。最简单的方法是使用puro
(opens in a new tab) - Plasmo的上下文实用库。通过将库添加到你的package.json
文件并运行pnpm i
来安装它:
{
...
"dependencies": {
...
"puro": "0.3.4"
}
}
然后,我们可以创建我们的提供者:
core/user-info.tsx
(opens in a new tab)
import { createProvider } from "puro"
import { useContext, useEffect, useState } from "react"
const useUserInfoProvider = () => {
const [userInfo, setUserInfo] = useState<chrome.identity.UserInfo>(null)
useEffect(() => {
chrome.identity.getProfileUserInfo((data) => {
if (data.email && data.id) {
setUserInfo(data)
}
})
}, [])
return userInfo
}
const { BaseContext, Provider } = createProvider(useUserInfoProvider)
export const useUserInfo = () => useContext(BaseContext)
export const UserInfoProvider = Provider
并在我们的弹出窗口中使用它:
popup.tsx
(opens in a new tab)
import { UserInfoProvider, useUserInfo } from "~core/user-info"
const EmailShowcase = () => {
const userInfo = useUserInfo()
return (
<div>
你的电子邮件是: <b>{userInfo?.email}</b>
</div>
)
}
function IndexPopup() {
return (
<UserInfoProvider>
<div
style={{
display: "flex",
flexDirection: "column",
padding: 16
}}>
<h1>
欢迎使用你的 <a href="https://www.plasmo.com">Plasmo</a> 扩展!
</h1>
<EmailShowcase />
</div>
</UserInfoProvider>
)
}
export default IndexPopup
将Stripe链接集成到弹出页面
为了简化Stripe支付链接与身份API的流程,我们可以通过它们的API参数 (opens in a new tab)预填Stripe托管表单上的电子邮件,该电子邮件是从上面的UserInfoProvicer
获得的。在我们将用户重定向到Stripe支付之前,让我们也调用OAuth流程,以确保客户同意我们使用他们的电子邮件地址。这还将为我们的扩展启动访问令牌缓存,使未来的调用无需交互。
popup.tsx
(opens in a new tab)
<button
disabled={!userInfo}
onClick={async () => {
chrome.identity.getAuthToken(
{
interactive: true
},
(token) => {
if (!!token) {
window.open(
`${process.env.PLASMO_PUBLIC_STRIPE_LINK}?client_reference_id=${
userInfo.id
}&prefilled_email=${encodeURIComponent(userInfo.email)}`,
"_blank"
)
}
}
)
}}>
订阅付费功能
</button>
验证订阅并启用一些高级功能
我们现在将设置后端以验证用户的订阅。我们可以通过利用NextJS与Plasmo的互操作性来简化此过程。我们将首先安装NextJS和一些实用库:
{
"scripts": {
"start": "next start",
"dev": "run-p dev:*",
"dev:plasmo": "plasmo dev",
"dev:next": "next dev --port 8472",
"build": "run-p build:*",
"build:plasmo": "plasmo build",
"build:next": "next build"
},
...
"dependencies": {
...
"next": "12.1.6",
"google-auth-library": "8.0.2",
"swr": "1.3.0",
"stripe": "9.8.0"
},
"devDependencies": {
...
"@plasmohq/rps": "1.3.4",
}
}
@plasmohq/rps
是来自Plasmo的帮助库,用于促进脚本的并行或顺序运行。它是npm-run-all (opens in a new tab)的现代化分支。
一旦我们设置了依赖项,让我们创建一些实用函数:
然后,我们创建两个API路由:一个用于检查用户的订阅,另一个用于调用高级功能。这两个API路由都必须首先解析授权头中的访问令牌,然后使用令牌独立地获取用户资料,再使用资料的数据获取用户的订阅。
pages/api/check-subscription.ts
(opens in a new tab)pages/api/premium-feature.ts
(opens in a new tab)
要从我们的扩展调用开发服务器,我们可以使用环境变量存储API URI,并在manifest主机中引用它:
PLASMO_PUBLIC_API_URI=http://localhost:8472
...
{
...
"manifest": {
...
"host_permissions": [
"$PLASMO_PUBLIC_API_URI/*",
"https://*/*"
]
}
}
终止并重新运行pnpm dev
以同时启动后端和扩展的开发服务器。在调用我们的API之前,让我们设置更多的客户端助手:
现在,我们可以使用swr
来调用和重新验证弹出窗口中的check-subscription
API:
import useSWR from "swr"
import { callAPI } from "~core/premium-api"
...
const { data, error } = useSWR<{ active: boolean }>(
"/api/check-subscription",
callAPI
)
if (!!error || !data?.active) {
// 没有活动订阅,显示付款按钮
}
// 有活动订阅,显示高级功能按钮
然后,调用我们的高级功能:
<button
onClick={async () => {
const data = await callAPI("/api/premium-feature", {
method: "POST"
})
alert(data.code)
}}>
调用超棒的高级功能
</button>
完整示例
有关完整的示例,请查看GitHub存储库中的with-stripe (opens in a new tab)。