Lewati ke konten utama

Integrasi React

PAY.ID menyediakan paket React khusus — payid-react — yang dibangun di atas wagmi. Menangani semua interaksi kontrak, koneksi wallet, dan state multi-step payment untukmu.


Install

npm install payid-react payid wagmi viem @tanstack/react-query ethers
# atau
bun add payid-react payid wagmi viem @tanstack/react-query ethers

Untuk render QR image (opsional — payload string selalu tersedia):

npm install qrcode
npm install --save-dev @types/qrcode

Setup Provider

// main.tsx
import { WagmiProvider, createConfig, http } from 'wagmi'
import { hardhat } from 'wagmi/chains'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { PayIDProvider } from 'payid-react'

const wagmiConfig = createConfig({
chains: [hardhat],
transports: { [hardhat.id]: http() },
})
const queryClient = new QueryClient()

export default function Root() {
return (
<WagmiProvider config={wagmiConfig}>
<QueryClientProvider client={queryClient}>
<PayIDProvider
contracts={{
31337: {
ruleAuthority: '0x322813Fd9A801c5507c9de605d63CEA4f2CE6c44',
ruleItemERC721: '0xa85233C63b9Ee964Add6F2cffe00Fd84eb32338f',
combinedRuleStorage: '0x9A9f2CCfdE556A7E9Ff0848998Aa4a0CFD8863AE',
payIDVerifier: '0x59b670e9fA9D0A427751Af201D676719a970857b',
payWithPayID: '0x4ed7c70F96B99c776995fB64377f0d4aB3B0e1C1',
},
}}
>
<App />
</PayIDProvider>
</QueryClientProvider>
</WagmiProvider>
)
}

Ringkasan Hooks

HookSisiFungsi
usePayIDFlowPayerFull payment flow — fetch rules, evaluasi, prove, submit
usePayIDQRMerchantGenerate QR code bertanda tangan
usePayETHPayerLow-level: bayar dengan ETH
usePayERC20PayerLow-level: bayar dengan ERC20
useSubscribeMerchantSubscribe untuk unlock slot rule
useCreateRuleMerchantBuat Rule NFT baru
useActivateRuleMerchantAktifkan (mint) Rule NFT
useExtendRuleExpiryMerchantPerpanjang expiry Rule NFT
useRegisterCombinedRuleMerchantDaftarkan kebijakan combined rule
useDeactivateCombinedRuleMerchantNonaktifkan kebijakan aktif
useSubscriptionReadCek status subscription
useMyRulesReadAmbil Rule NFT milikmu
useActiveCombinedRuleReadBaca kebijakan aktif alamat manapun
useAllCombinedRulesReadList semua kebijakan terdaftar

usePayIDFlow — Full Payment Flow (Payer)

Cara paling mudah untuk bayar. Menangani semua langkah: load rules dari chain + IPFS, evaluasi, sign proof, ERC20 approval, dan submit transaksi.

import { usePayIDFlow } from 'payid-react'
import { parseUnits, zeroAddress } from 'viem'

const USDC_ADDRESS = '0xc6e7DF5E7b4f2A278906862b61205850344D4e7d'

function CheckoutButton({ merchantAddress }: { merchantAddress: `0x${string}` }) {
const { execute, reset, status, isPending, isSuccess, error, denyReason, txHash } =
usePayIDFlow()

return (
<div>
<button
onClick={() => execute({
receiver: merchantAddress,
asset: USDC_ADDRESS,
amount: parseUnits('50', 6),
payId: 'pay.id/merchant',
})}
disabled={isPending}
>
{status === 'idle' && 'Bayar 50 USDC'}
{status === 'fetching-rule' && 'Memuat rules...'}
{status === 'evaluating' && 'Mengecek rules...'}
{status === 'proving' && 'Tanda tangani proof di wallet...'}
{status === 'approving' && 'Setujui USDC...'}
{status === 'awaiting-wallet' && 'Konfirmasi payment...'}
{status === 'confirming' && 'Konfirmasi di chain...'}
{status === 'success' && '✅ Berhasil!'}
{status === 'denied' && '❌ Ditolak'}
{status === 'error' && 'Coba lagi'}
</button>

{status === 'denied' && <p>Alasan: {denyReason}</p>}
{status === 'success' && <p>TX: {txHash}</p>}
{error && <p style={{ color: 'red' }}>{error}</p>}
</div>
)
}

Payment ETH

Pass zeroAddress sebagai asset untuk ETH native:

execute({ receiver: merchantAddress, asset: zeroAddress, amount: parseUnits('0.01', 18), payId: 'pay.id/merchant' })

usePayIDQR — QR Code Generator (Merchant)

Merchant pakai hook ini untuk generate QR code bertanda tangan yang discan pelanggan.

import { usePayIDQR } from 'payid-react'
import { parseUnits } from 'viem'

function MerchantQR() {
const { generate, reset, isPending, isReady, payload, qrDataUrl, error } = usePayIDQR()

return (
<div>
<button
onClick={() => generate({
payId: 'pay.id/toko-ku',
allowedAsset: USDC_ADDRESS,
maxAmount: parseUnits('100', 6),
expiresAt: Math.floor(Date.now() / 1000) + 3600,
})}
disabled={isPending}
>
{isPending ? 'Membuat QR...' : 'Generate QR'}
</button>

{/* PNG data URL kalau package 'qrcode' terinstall */}
{qrDataUrl && <img src={qrDataUrl} alt="Scan untuk bayar" width={256} />}

{/* Fallback: pakai library QR apapun dengan payload string */}
{payload && !qrDataUrl && <p style={{ wordBreak: 'break-all', fontSize: 11 }}>{payload}</p>}

{error && <p style={{ color: 'red' }}>{error}</p>}
{isReady && <button onClick={reset}>QR Baru</button>}
</div>
)
}

Di sisi payer setelah scan:

import { decodeSessionPolicyV2QR } from 'payid/sessionPolicy'

const policy = decodeSessionPolicyV2QR(scannedQRString)
execute({
receiver: policy.receiver as `0x${string}`,
asset: policy.allowedAsset as `0x${string}`,
amount: BigInt(policy.maxAmount),
payId: policy.payId,
})

Write Hooks Merchant

useSubscribe — Subscribe untuk unlock 3 slot rule

const { subscribe, isPending } = useSubscribe()
// Ambil harga dari oracle dulu via useReadContract, lalu:
subscribe(price as bigint)

useCreateRule + useActivateRule

const { createRule }   = useCreateRule()
const { activateRule } = useActivateRule()

createRule({ ruleHash: keccak256(toBytes(ruleJson)), uri: 'ipfs://Qm...' })
activateRule(ruleId) // ruleId dari RuleCreated event

useExtendRuleExpiry — Perpanjang sebelum expired

const { extendRuleExpiry } = useExtendRuleExpiry()

extendRuleExpiry({
tokenId,
newExpiry: BigInt(Math.floor(Date.now() / 1000) + 30 * 24 * 60 * 60),
priceInWei: 100_000_000_000_000n,
})

useRegisterCombinedRule — Aktifkan kebijakan payment

const { registerCombinedRule } = useRegisterCombinedRule()

registerCombinedRule({
ruleSetHash: keccak256(toBytes('my-policy-v1')),
ruleNFTs: Array(tokenIds.length).fill(contracts.ruleItemERC721),
tokenIds,
version: 1n,
})

Read Hooks

useSubscription — Status langganan

const { data: sub } = useSubscription(address)
// sub.isActive, sub.logicalRuleCount, sub.maxSlots, sub.expiry

useMyRules — Rule NFT milikmu

const { data: rules = [] } = useMyRules()
// rules[].ruleId, rules[].active, rules[].tokenId

useActiveCombinedRule — Kebijakan aktif merchant

const { data: policy } = useActiveCombinedRule(merchantAddress)
// policy.hash, policy.version, policy.ruleRefs

Tips

Jangan cache proof. usePayIDFlow generate fresh proof setiap kali. Proof kedaluwarsa dalam ttlSeconds detik (default 300).

Cek subscription sebelum buat rule. Pakai useSubscription(address) untuk verifikasi user punya slot aktif.

Kompatibel dengan semua wallet. usePayIDFlow dan usePayIDQR pakai useConnectorClient dari wagmi — kompatibel dengan MetaMask, WalletConnect, Coinbase Wallet, Safe, dan connector wagmi lainnya.