Skip to content

Commit d12af42

Browse files
committed
adds payment links to documentation
1 parent 6a7dd39 commit d12af42

File tree

8 files changed

+506
-0
lines changed

8 files changed

+506
-0
lines changed

docs/pos/payments/onramps.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Onramps
2+
3+
Onramps are the critical piece of payments. The majority of friction for payments with crypto comes from the challenging
4+
of onboarding your users in a safe way.
5+
6+
Below are options for onboarding with accompanying code examples. Onboarding involves handling sensitive information and is best conducted server-side:
7+
8+
#
9+
10+
11+
12+
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# BlindPay
2+
3+
BlindPay has a onramp tutorial based on Polygon Amoy testnet [available here.](https://blindpay.com/docs/getting-started/quick-start-payin)
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
# Stripe
2+
3+
!!! info Stripe Documentation
4+
5+
[Full documentation available on Stripe.](https://docs.stripe.com/)
6+
7+
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 and you can use the following code to onboarding users to USDC on Polygon:
8+
9+
### Prerequisites
10+
11+
* [Stripe API access](https://docs.stripe.com/api)
12+
* [Stripe SDK](https://github.com/stripe/stripe-node)
13+
14+
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+
65+
## Serving to Frontend
66+
67+
`client_secret` can now be served to the frontend:
68+
69+
```html
70+
<script src="https://js.stripe.com/v3/crypto/onramp.js"></script>
71+
<div id="onramp"></div>
72+
<script>
73+
(async () => {
74+
// Fetch client_secret from your server
75+
const { client_secret } = await fetch("/api/create-onramp-session").then(r => r.json());
76+
77+
const onramp = await window.StripeOnramp.init({
78+
clientSecret: client_secret,
79+
appearance: { /* optional branding overrides */ },
80+
});
81+
82+
onramp.mount("#onramp");
83+
})();
84+
</script>
85+
```
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# USDC
2+
3+
## Prerequisites
4+
5+
* Use (Circle’s sandbox environment)[https://app-sandbox.circle.com/signin] for development and testing. Production requires a separate API key and account setup. 
6+
* Authenticate HTTP requests via (the Authorization: Bearer YOUR_API_KEY header.)[https://developers.circle.com/circle-mint/getting-started-with-the-circle-apis?utm_source=chatgpt.com#api-key-authentication]
7+
* (Circle SDK)[https://developers.circle.com/circle-mint/circle-sdks]
8+
!!! info
9+
Keep your API key secure! Never expose it in client-side code. 
10+
11+
## Minting USDC
12+
13+
```js
14+
import { Circle, CircleEnvironments } from '@circle-fin/circle-sdk';
15+
16+
const circle = new Circle(
17+
process.env.CIRCLE_API_KEY || '',
18+
CircleEnvironments.sandbox
19+
);
20+
21+
async function mintUSDC() {
22+
const res = await circle.paymentIntents.createPaymentIntent({
23+
idempotencyKey: 'unique-key-12345',
24+
amount: { amount: '100.00', currency: 'USD' },
25+
settlementCurrency: 'USD',
26+
paymentMethods: [
27+
{ chain: 'POLY', type: 'blockchain' }
28+
],
29+
});
30+
31+
console.log(res);
32+
}
33+
34+
mintUSDC().catch(console.error);
35+
```
36+
37+
##

docs/pos/payments/overview.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Polygon has a dozens of payment options for developers. In this section, we'll cover the basic architecture of payment flows to better understand how they work.
2+
3+
We're also highlighting some of the best options available on Polygon today.
4+
5+
!!! info
6+
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.

docs/pos/payments/payments.md

Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
## USDC
2+
USDC is ranked as the 7th largest cryptocurrency, with a circulation of $65 billion. Nearly 5 million monthly active users engage with USDC on Polygon protocols. Cost-efficient transactions on Polygon makes it the default chain for USDC payments, especially micro or high-volume payments.
3+
4+
### Integrating USDC
5+
Circle has [excellent documentation,](https://developers.circle.com/) including ready-to-use SDKs and sample applications
6+
7+
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**
8+
9+
### Native USDC integration:
10+
11+
USDC is an ERC-20 contract with the standard `approve`/`transfer` process
12+
13+
```ts
14+
// pnpm add viem
15+
import { createPublicClient, createWalletClient, http, parseUnits } from "viem";
16+
import { polygon } from "viem/chains";
17+
import { privateKeyToAccount } from "viem/accounts";
18+
19+
// Native USDC on Polygon PoS (NOT the old bridged USDC.e)
20+
// Source: Circle "USDC Contract Addresses"
21+
const USDC = "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359";
22+
23+
// Testnet USDC on Amoy
24+
// const USDC = "0x41E94Eb019C0762f9Bfcf9Fb1E58725BfB0e7582";
25+
26+
const erc20 = [
27+
{ type: "function", name: "decimals", stateMutability: "view", inputs: [], outputs: [{ type: "uint8" }] },
28+
{ type: "function", name: "balanceOf", stateMutability: "view", inputs: [{ type: "address" }], outputs: [{ type: "uint256" }] },
29+
{ type: "function", name: "approve", stateMutability: "nonpayable", inputs: [{ type: "address" }, { type: "uint256" }], outputs: [{ type: "bool" }] },
30+
{ type: "function", name: "transfer", stateMutability: "nonpayable", inputs: [{ type: "address" }, { type: "uint256" }], outputs: [{ type: "bool" }] },
31+
];
32+
33+
const account = privateKeyToAccount(process.env.PRIV_KEY as `0x${string}`);
34+
35+
const rpc = http(process.env.POLYGON_RPC_URL); // e.g. https://polygon-rpc.com
36+
const pub = createPublicClient({ chain: polygon, transport: rpc });
37+
const wallet = createWalletClient({ chain: polygon, transport: rpc, account });
38+
39+
async function main() {
40+
const me = account.address as `0x${string}`;
41+
const decimals = await pub.readContract({ address: USDC, abi: erc20, functionName: "decimals" });
42+
const bal = await pub.readContract({ address: USDC, abi: erc20, functionName: "balanceOf", args: [me] });
43+
console.log("USDC balance:", Number(bal) / 10 ** Number(decimals));
44+
45+
// Transfer 1 USDC
46+
const amount = parseUnits("1", Number(decimals));
47+
const hash = await wallet.writeContract({ address: USDC, abi: erc20, functionName: "transfer", args: ["0xRecipient...", amount] });
48+
console.log("tx:", hash);
49+
}
50+
main();
51+
```
52+
53+
### Gateway integration:
54+
55+
```ts
56+
57+
import { createPublicClient, createWalletClient, http, parseUnits } from "viem";
58+
import { polygon } from "viem/chains";
59+
import { privateKeyToAccount } from "viem/accounts";
60+
import { fetch } from "undici";
61+
62+
const USDC = "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359";
63+
const GATEWAY_WALLET = "0x77777777Dcc4d5A8B6E418Fd04D8997ef11000eE";
64+
const GATEWAY_MINTER = "0x2222222d7164433c4C09B0b0D809a9b52C04C205";
65+
66+
const erc20 = [
67+
{ type: "function", name: "decimals", stateMutability: "view", inputs: [], outputs: [{ type: "uint8" }] },
68+
{ type: "function", name: "approve", stateMutability: "nonpayable", inputs: [{ type: "address" }, { type: "uint256" }], outputs: [{ type: "bool" }] },
69+
];
70+
71+
const gatewayWalletAbi = [
72+
{ type: "function", name: "deposit", stateMutability: "nonpayable", inputs: [{ type: "address" }, { type: "uint256" }], outputs: [] },
73+
];
74+
75+
const gatewayMinterAbi = [
76+
{ type: "function", name: "gatewayMint", stateMutability: "nonpayable", inputs: [{ type: "bytes" }, { type: "bytes" }], outputs: [] },
77+
];
78+
79+
const account = privateKeyToAccount(process.env.PRIV_KEY as `0x${string}`);
80+
const rpc = http(process.env.POLYGON_RPC_URL);
81+
const pub = createPublicClient({ chain: polygon, transport: rpc });
82+
const wallet = createWalletClient({ chain: polygon, transport: rpc, account });
83+
84+
async function gatewayTransfer() {
85+
// 1) Approve + deposit into GatewayWallet on Polygon
86+
const decimals = await pub.readContract({ address: USDC, abi: erc20, functionName: "decimals" });
87+
const amount = parseUnits("25", Number(decimals)); // example
88+
89+
await wallet.writeContract({ address: USDC, abi: erc20, functionName: "approve", args: [GATEWAY_WALLET, amount] });
90+
await wallet.writeContract({ address: GATEWAY_WALLET, abi: gatewayWalletAbi, functionName: "deposit", args: [USDC, amount] });
91+
92+
// 2) Request attestation from Circle Gateway API
93+
// Build and sign a BurnIntent (EIP-712 typed data) for source=Polygon, destination=Polygon (same-chain "withdraw") or another chain.
94+
// For brevity, assume you already produced `burnIntent` and EIP-712 signature with your EOA.
95+
const res = await fetch("https://gateway-api.circle.com/v1/transfer", {
96+
method: "POST",
97+
headers: { "content-type": "application/json", authorization: `Bearer ${process.env.CIRCLE_API_KEY}` },
98+
body: JSON.stringify({
99+
burnIntent: {
100+
maxBlockHeight: "999999999999",
101+
maxFee: "0",
102+
spec: {
103+
version: 1,
104+
sourceDomain: 7, // Polygon PoS domain id (per Circle docs)
105+
destinationDomain: 7, // or another supported domain id
106+
sourceContract: GATEWAY_WALLET,
107+
destinationContract: GATEWAY_MINTER,
108+
sourceToken: USDC,
109+
destinationToken: USDC,
110+
sourceDepositor: account.address,
111+
destinationRecipient: "0xRecipient...", // who receives on destination
112+
sourceSigner: account.address,
113+
destinationCaller: "0x0000000000000000000000000000000000000000",
114+
value: amount.toString(),
115+
},
116+
},
117+
// include your EIP-712 signature of BurnIntent here if required by your flow
118+
}),
119+
});
120+
const { attestationPayload, signature } = await res.json();
121+
122+
// 3) Submit attestation to GatewayMinter on destination chain
123+
const tx = await wallet.writeContract({
124+
address: GATEWAY_MINTER,
125+
abi: gatewayMinterAbi,
126+
functionName: "gatewayMint",
127+
args: [attestationPayload as `0x${string}`, signature as `0x${string}`],
128+
});
129+
console.log("gatewayMint tx:", tx);
130+
}
131+
132+
gatewayTransfer();
133+
```
134+
135+
## Tether
136+
137+
USDT makes up over half of the stablecoins on Polygon and has been upgraded from USDT to Polygon-native USDT0, the omnichain deployment of USDT.
138+
139+
### Integrating Tether
140+
141+
Tether is an ERC-20 and follows the common `approve`/`transfer` flow:
142+
143+
144+
145+
```ts
146+
import { createPublicClient, createWalletClient, http, parseUnits } from "viem";
147+
import { polygon } from "viem/chains";
148+
import { privateKeyToAccount } from "viem/accounts";
149+
150+
// Mainnet, No testnet version of USDT
151+
const USDT = "0xc2132d05d31c914a87c6611c10748aeb04b58e8f";
152+
const erc20 = [
153+
{ type: "function", name: "decimals", stateMutability: "view", inputs: [], outputs: [{ type: "uint8" }] },
154+
{ type: "function", name: "balanceOf", stateMutability: "view", inputs: [{ type: "address" }], outputs: [{ type: "uint256" }] },
155+
{ type: "function", name: "approve", stateMutability: "nonpayable", inputs: [{ type: "address" }, { type: "uint256" }], outputs: [{ type: "bool" }] },
156+
{ type: "function", name: "transfer", stateMutability: "nonpayable", inputs: [{ type: "address" }, { type: "uint256" }], outputs: [{ type: "bool" }] },
157+
];
158+
159+
const account = privateKeyToAccount(process.env.PRIV_KEY as `0x${string}`);
160+
const rpc = http(process.env.POLYGON_RPC_URL);
161+
const pub = createPublicClient({ chain: polygon, transport: rpc });
162+
const wallet = createWalletClient({ chain: polygon, transport: rpc, account });
163+
164+
async function main() {
165+
const me = account.address as `0x${string}`;
166+
const decimals = await pub.readContract({ address: USDT, abi: erc20, functionName: "decimals" });
167+
const bal = await pub.readContract({ address: USDT, abi: erc20, functionName: "balanceOf", args: [me] });
168+
console.log("USDT balance:", Number(bal) / 10 ** Number(decimals));
169+
170+
// Transfer 1 USDT
171+
const amount = parseUnits("1", Number(decimals));
172+
const hash = await wallet.writeContract({
173+
address: USDT,
174+
abi: erc20,
175+
functionName: "transfer",
176+
args: ["0xRecipient...", amount],
177+
});
178+
console.log("tx:", hash);
179+
}
180+
main();
181+
```
182+
183+
## Stripe
184+
185+
186+
187+
## Bridge
188+
189+
Bridge is a paid enterprise developer platform that joins together fiat and stablecoin rails. The product requires API access. Once granted, you can create virtual accounts, setup transfers, and even establish credit cards for your users.
190+
191+
### Prerequisites
192+
193+
You need an API key and a unique Idempotency-Key
194+
for each POST which you can get through [the Bridge dashboard.](https://apidocs.bridge.xyz/api-reference/introduction/authentication)
195+
196+
### Create a USD → USDC transfer on Polygon
197+
198+
```curl
199+
--request POST https://api.bridge.xyz/v0/transfers \
200+
--header "Api-Key: $BRIDGE_API_KEY" \
201+
--header "Content-Type: application/json" \
202+
--header "Idempotency-Key: $(uuidgen)" \
203+
--data '{
204+
"client_reference_id": "order-1234",
205+
"amount": "25.00",
206+
"on_behalf_of": "cust_abc123",
207+
"source": {
208+
"currency": "usd",
209+
"payment_rail": "ach"
210+
},
211+
"destination": {
212+
"currency": "usdc",
213+
"payment_rail": "polygon",
214+
"to_address": "0xRecipientAddressHere"
215+
}
216+
}'
217+
```

0 commit comments

Comments
 (0)