Book

 · about 1 month ago

x402 payment js - Nov 2 15:30

import { Controller } from "@hotwired/stimulus"

export default class extends Controller { static values = { url: String }

async pay() { if (!window.ethereum) { alert("⚠️ 请先安装钱包") return }

try {
// 1️⃣ 请求资源
const res = await fetch(this.urlValue)
const payInfo = await res.json()

if (res.status !== 402) {
alert("✅ 已解锁或无需支付")
return
}

// 2️⃣ 从返回中取出支付信息
const accept = payInfo.accepts[0]
const { payTo, asset, maxAmountRequired, network } = accept

console.log("🧾 支付信息:", accept)
console.log("💰 需支付金额(最小单位):", maxAmountRequired)

// 3️⃣ 连接钱包
const [from] = await window.ethereum.request({
method: "eth_requestAccounts"
})

// 4️⃣ 构造 ERC20 transfer data
// ✅ 直接使用 maxAmountRequired(已经是最小单位)
const amountInUnits = BigInt(maxAmountRequired)

const selector = "0xa9059cbb"
const toPadded = payTo.replace(/^0x/, "").padStart(64, "0")
const amountHex = amountInUnits.toString(16).padStart(64, "0")
const data = selector + toPadded + amountHex

console.log("📦 交易 data:", data)
console.log("📊 金额 hex:", amountHex, "=", amountInUnits.toString(), "最小单位")

// 5️⃣ 发交易
const txHash = await window.ethereum.request({
method: "eth_sendTransaction",
params: [{
from,
to: asset, // ✅ USDC 合约地址
data
}]
})

console.log("💸 TxHash:", txHash)

// 6️⃣ 构造 X-PAYMENT payload
const paymentPayload = {
x402Version: 1,
scheme: "exact",
network: network || "base-sepolia",
payload: {
authorization: {
from,
to: payTo,
value: maxAmountRequired, // ✅ 保持原始值
txHash,
timestamp: Math.floor(Date.now() / 1000)
}
}
}

const encoded = btoa(JSON.stringify(paymentPayload))
console.log("🔐 Payment Payload:", paymentPayload)

// 7️⃣ 验证支付
const verifyRes = await fetch(this.urlValue, {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-PAYMENT": encoded
},
body: JSON.stringify({})
})

if (verifyRes.ok) {
const data = await verifyRes.json()
alert("✅ 支付成功!内容已解锁")
console.log("📄 内容:", data)
window.location.reload()
} else {
const err = await verifyRes.text()
console.error("❌ 支付验证失败:", err)
alert(`❌ 支付验证失败: ${err}`)
}

} catch (e) {
console.error("💥 出错:", e)
alert("💥 支付失败: " + e.message)
}


} }