Skip to content

Commit 3737703

Browse files
authored
Merge pull request 0xPolygon#2696 from 0xPolygon/payments-addition
Payments addition
2 parents 4c8e90b + 911b93a commit 3737703

File tree

4 files changed

+268
-0
lines changed

4 files changed

+268
-0
lines changed
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
# Stripe
2+
!!! info
3+
NOTE: Like all of crypto, payments require handling sensitive data. The following examples are demonstrations of integrations but should not be used in production. In production, sensitive information such as API keys, private-key wallets, etc., should be put in a secrets manager or vault.
4+
5+
!!! info Stripe Documentation
6+
7+
[Full documentation available on Stripe.](https://docs.stripe.com/)
8+
9+
Stripe integration requires a US domicile (excluding Hawaii) and a registration for your business. Once that registration has cleared, the API `Crypto/Onramps_sessions` will be enabled. Below is an example using Stripe's Sandbox environment:
10+
11+
### Prerequisites
12+
13+
* [Stripe API access](https://docs.stripe.com/api)
14+
* [Stripe SDK](https://github.com/stripe/stripe-node)
15+
16+
### Server
17+
18+
```curl
19+
# SERVER-SIDE ONLY
20+
curl https://api.stripe.com/v1/crypto/onramp_sessions \
21+
-u sk_test_your_secret_key: \
22+
-d "destination_currency"="usdc" \
23+
-d "destination_network"="polygon" \
24+
-d "wallet_addresses[0][type]"="self_custody" \
25+
-d "wallet_addresses[0][address]"="0xYOUR_POLYGON_ADDRESS"
26+
```
27+
28+
If successful, the server will return `client_secret` in a format similar to this:
29+
30+
```json
31+
{
32+
"id": "cos_0MYvmj589O8KAxCGp14dTjiw",
33+
"object": "crypto.onramp_session",
34+
"client_secret": "cos_0MYvmj589O8KAxCGp14dTjiw_secret_BsxEqQLiYKANcTAoVnJ2ikH5q002b9xzouk",
35+
"created": 1675794053,
36+
"livemode": false,
37+
[...]
38+
}
39+
}
40+
```
41+
42+
Once you have the `client_secret`, you can pass that to the frontend. (Note: Only pass the `client_secret`, not the `STRIPE_SECRET_KEY`)
43+
44+
### Node server
45+
46+
```js
47+
import Stripe from "stripe";
48+
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);
49+
50+
export async function createOnrampSession(req, res) {
51+
const session = await stripe.crypto.onramps.sessions.create({
52+
destination_currency: "usdc",
53+
destination_network: "polygon",
54+
wallet_addresses: [{
55+
type: "self_custody",
56+
address: "0xYOUR_POLYGON_ADDRESS"
57+
}],
58+
// optional: customer, email, reference, etc.
59+
});
60+
res.json({ client_secret: session.client_secret });
61+
}
62+
```
63+
64+
## Serving to Frontend
65+
66+
`client_secret` can now be served to the frontend:
67+
68+
```html
69+
<script src="https://js.stripe.com/v3/crypto/onramp.js"></script>
70+
<div id="onramp"></div>
71+
<script>
72+
(async () => {
73+
// Fetch client_secret from your server
74+
const { client_secret } = await fetch("/api/create-onramp-session").then(r => r.json());
75+
76+
const onramp = await window.StripeOnramp.init({
77+
clientSecret: client_secret,
78+
appearance: { /* optional branding overrides */ },
79+
});
80+
81+
onramp.mount("#onramp");
82+
})();
83+
</script>
84+
```

docs/pos/payments/overview.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
Launch payments on Polygon to benefit from ultra low cost transactions, a compliant and reliable network, and worldwide distribution.
2+
3+
## Exchanges, On- and Offramps
4+
Polygon is the choice for major payment projects due to the hundreds of third-party options available on the network. Integrate with exchanges and on- and offramps to get crypto-to-fiat exposure in 150+ countries.
5+
6+
Some of the major payment infrastructure projects Polygon supports:
7+
8+
* **Payments and Ramps:** [Stripe](https://docs.stripe.com/crypto/stablecoin-payments),
9+
[Shopify](https://depay.com/plugins/shopify), [MoonPay](https://dev.moonpay.com/docs/on-ramp-overview), [Bridge](https://apidocs.bridge.xyz/), [Revolut](https://www.revolut.com/en-US/), [BlindPay](https://blindpay.com/docs/getting-started/quick-start-payin), [Payy](https://payy.link/), [Avenia](https://avenia.io/), [Crossmint](https://crossmint.io), [MoonPay](https://dev.moonpay.com/docs/on-ramp-overview), [Transak](https://docs.transak.com/), [Ramp](https://docs.rampnetwork.com/)
10+
11+
[Learn how to integrate Stripe on Polygon here.](./onramps/onramps-stripe.md)
12+
13+
* **Stablecoins:** [USDC](https://developers.circle.com/), [USDT](https://tether.to/en/why-tether), Agora, Axelar, [DAI](https://docs.makerdao.com/smart-contract-modules/dai-module/dai-detailed-documentation), [FRAX](https://docs.frax.finance/fraxlend/abi-and-code)
14+
15+
16+
## Fastest, Ultra-Low Cost Transactions
17+
18+
With the upgrade to Heimdall v2, deterministic finality on Polygon chain is now achieved between 2-5 seconds thanks to the 1-2 seconds block time in Heimdall, meaning miletones are voted on and finalized much faster.
19+
20+
Fast finality means payments will be immediately available for your users.
21+
22+
[Read more about finality here.](../concepts/finality/finality.md)
23+
24+
Polygon transaction are also incredibly low-cost, around 0.0001 in cost. [You can track the average price of Polygon transactions here.](https://polygonscan.com/chart/avg-txfee-usd)
25+
26+
## 99.999% Uptime
27+
28+
Polygon is a rock of stability that provides 99.999% network uptime. [You can always check the status of Polygon's mainnet and testnet network here.](https://status.polygon.technology/)
29+
30+
## Compliance
31+
32+
Business payments require serious compliance. Polygon has the major providers on its platform, including:
33+
34+
[Sumsub](https://docs.sumsub.com/docs/overview), [Onfido](https://documentation.onfido.com/), [Persona](https://docs.withpersona.com/getting-started), [Chainanalysis](https://www.chainalysis.com/), [TRM](https://www.trmlabs.com/), [Forta](https://docs.forta.network/en/latest/), [Elliptic](https://developers.elliptic.co/docs/getting-started)
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
!!! info
2+
NOTE: Like all of crypto, payments require handling sensitive data. The following examples are demonstrations of integrations but should not be used in production. In production, sensitive information such as API keys, private-key wallets, etc., should be put in a secrets manager or vault.
3+
4+
## USDC
5+
USDC is a [top stablecoin used for payments on Polygon.](https://defillama.com/stablecoins/Polygon) Cost-efficient transactions on Polygon makes it the default chain for USDC payments, especially micro or high-volume payments.
6+
7+
### Integrating USDC
8+
Circle has [excellent documentation,](https://developers.circle.com/) including ready-to-use SDKs and sample applications
9+
10+
For those looking to easily integrate USDC to Polygon, we have two options below: **Native USDC integration** or crosschain USDC using Circle’s **Gateway integration**
11+
12+
### Native USDC integration:
13+
14+
USDC is an ERC-20 contract with the standard `approve`/`transfer` process
15+
16+
```ts
17+
// pnpm add viem
18+
import { createPublicClient, createWalletClient, http, parseUnits } from "viem";
19+
import { polygon } from "viem/chains";
20+
import { privateKeyToAccount } from "viem/accounts";
21+
22+
// Native USDC on Polygon PoS (NOT the old bridged USDC.e)
23+
// Source: Circle "USDC Contract Addresses"
24+
const USDC = "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359";
25+
26+
// Testnet USDC on Amoy
27+
// const USDC = "0x41E94Eb019C0762f9Bfcf9Fb1E58725BfB0e7582";
28+
29+
const erc20 = [
30+
{ type: "function", name: "decimals", stateMutability: "view", inputs: [], outputs: [{ type: "uint8" }] },
31+
{ type: "function", name: "balanceOf", stateMutability: "view", inputs: [{ type: "address" }], outputs: [{ type: "uint256" }] },
32+
{ type: "function", name: "approve", stateMutability: "nonpayable", inputs: [{ type: "address" }, { type: "uint256" }], outputs: [{ type: "bool" }] },
33+
{ type: "function", name: "transfer", stateMutability: "nonpayable", inputs: [{ type: "address" }, { type: "uint256" }], outputs: [{ type: "bool" }] },
34+
];
35+
36+
const account = privateKeyToAccount(process.env.PRIV_KEY as `0x${string}`);
37+
38+
const rpc = http(process.env.POLYGON_RPC_URL); // e.g. https://polygon-rpc.com
39+
const pub = createPublicClient({ chain: polygon, transport: rpc });
40+
const wallet = createWalletClient({ chain: polygon, transport: rpc, account });
41+
42+
async function main() {
43+
const me = account.address as `0x${string}`;
44+
const decimals = await pub.readContract({ address: USDC, abi: erc20, functionName: "decimals" });
45+
const bal = await pub.readContract({ address: USDC, abi: erc20, functionName: "balanceOf", args: [me] });
46+
console.log("USDC balance:", Number(bal) / 10 ** Number(decimals));
47+
48+
// Transfer 1 USDC
49+
const amount = parseUnits("1", Number(decimals));
50+
const hash = await wallet.writeContract({ address: USDC, abi: erc20, functionName: "transfer", args: ["0xRecipient...", amount] });
51+
console.log("tx:", hash);
52+
}
53+
main();
54+
```
55+
56+
### Gateway integration:
57+
58+
Gateway collects USDC balances across any chain with USDC. Users need to send USDC to the Gateway Wallet (not an ERC-20), [more details here.](https://developers.circle.com/gateway#setting-up-a-balance)
59+
60+
Once the users have a balance, they can send USDC to any chain using the Gateway implementation.
61+
62+
## Prerequisites
63+
64+
The follow example requires a Circle API key, which you can create through [the Circle Developer dashboard.](https://console.circle.com/) (You do not need a Cirlce license to get a sandbox API key).
65+
66+
```ts
67+
68+
import { createPublicClient, createWalletClient, http, parseUnits } from "viem";
69+
import { polygon } from "viem/chains";
70+
import { privateKeyToAccount } from "viem/accounts";
71+
import { fetch } from "undici";
72+
73+
const USDC = "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359";
74+
const GATEWAY_WALLET = "0x77777777Dcc4d5A8B6E418Fd04D8997ef11000eE";
75+
const GATEWAY_MINTER = "0x2222222d7164433c4C09B0b0D809a9b52C04C205";
76+
77+
const erc20 = [
78+
{ type: "function", name: "decimals", stateMutability: "view", inputs: [], outputs: [{ type: "uint8" }] },
79+
{ type: "function", name: "approve", stateMutability: "nonpayable", inputs: [{ type: "address" }, { type: "uint256" }], outputs: [{ type: "bool" }] },
80+
];
81+
82+
const gatewayWalletAbi = [
83+
{ type: "function", name: "deposit", stateMutability: "nonpayable", inputs: [{ type: "address" }, { type: "uint256" }], outputs: [] },
84+
];
85+
86+
const gatewayMinterAbi = [
87+
{ type: "function", name: "gatewayMint", stateMutability: "nonpayable", inputs: [{ type: "bytes" }, { type: "bytes" }], outputs: [] },
88+
];
89+
90+
const account = privateKeyToAccount(process.env.PRIV_KEY as `0x${string}`);
91+
const rpc = http(process.env.POLYGON_RPC_URL);
92+
const pub = createPublicClient({ chain: polygon, transport: rpc });
93+
const wallet = createWalletClient({ chain: polygon, transport: rpc, account });
94+
95+
async function gatewayTransfer() {
96+
// 1) Approve + deposit into GatewayWallet on Polygon
97+
const decimals = await pub.readContract({ address: USDC, abi: erc20, functionName: "decimals" });
98+
const amount = parseUnits("25", Number(decimals)); // example
99+
100+
await wallet.writeContract({ address: USDC, abi: erc20, functionName: "approve", args: [GATEWAY_WALLET, amount] });
101+
await wallet.writeContract({ address: GATEWAY_WALLET, abi: gatewayWalletAbi, functionName: "deposit", args: [USDC, amount] });
102+
103+
// 2) Request attestation from Circle Gateway API
104+
// Build and sign a BurnIntent (EIP-712 typed data) for source=Polygon, destination=Polygon (same-chain "withdraw") or another chain.
105+
// For brevity, assume you already produced `burnIntent` and EIP-712 signature with your EOA.
106+
const res = await fetch("https://gateway-api.circle.com/v1/transfer", {
107+
method: "POST",
108+
headers: { "content-type": "application/json", authorization: `Bearer ${process.env.CIRCLE_API_KEY}` },
109+
body: JSON.stringify({
110+
burnIntent: {
111+
maxBlockHeight: "999999999999",
112+
maxFee: "0",
113+
spec: {
114+
version: 1,
115+
sourceDomain: 7, // Polygon PoS domain id (per Circle docs)
116+
destinationDomain: 7, // or another supported domain id
117+
sourceContract: GATEWAY_WALLET,
118+
destinationContract: GATEWAY_MINTER,
119+
sourceToken: USDC,
120+
destinationToken: USDC,
121+
sourceDepositor: account.address,
122+
destinationRecipient: "0xRecipient...", // who receives on destination
123+
sourceSigner: account.address,
124+
destinationCaller: "0x0000000000000000000000000000000000000000",
125+
value: amount.toString(),
126+
},
127+
},
128+
// include your EIP-712 signature of BurnIntent here if required by your flow
129+
}),
130+
});
131+
const { attestationPayload, signature } = await res.json();
132+
133+
// 3) Submit attestation to GatewayMinter on destination chain
134+
const tx = await wallet.writeContract({
135+
address: GATEWAY_MINTER,
136+
abi: gatewayMinterAbi,
137+
functionName: "gatewayMint",
138+
args: [attestationPayload as `0x${string}`, signature as `0x${string}`],
139+
});
140+
console.log("gatewayMint tx:", tx);
141+
}
142+
143+
gatewayTransfer();
144+
```

mkdocs.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,12 @@ nav:
6161
- PoS to Ethereum: pos/how-to/bridging/ethereum-polygon/matic-to-ethereum.md
6262
- Submit mapping request: pos/how-to/bridging/ethereum-polygon/submit-mapping-request.md
6363
- Becoming a validator: pos/get-started/becoming-a-validator.md
64+
- Payments:
65+
- Overview: pos/payments/overview.md
66+
- Onramps:
67+
- Stripe: pos/payments/onramps/onramps-stripe.md
68+
- Transfers:
69+
- USDC: pos/transfers/transfers-usdc.md
6470
- Node how-tos:
6571
- Prerequisites: pos/how-to/prerequisites.md
6672
- Sync node using snapshots: pos/how-to/snapshots.md

0 commit comments

Comments
 (0)