如何將自定義加密支付方法集成到在線商店
每天 分享 最新 軟件 開發(fā) ,Devops,敏捷 ,測試 以及 項目 管理 最新 ,最熱門 的 文章 ,每天 花 3分鐘 學習 何樂而不為 ,希望 大家 點贊 ,加 關(guān)注 ,你的 支持 是我 最大 的 動力 。
電子商務(wù)店面在向客戶提供加密支付方式方面進展緩慢。加密支付插件或支付網(wǎng)關(guān)集成通常不可用,或者它們依賴第三方托管人收集、交換和分發(fā)資金。考慮到加密貨幣持有率和實驗比率的不斷增長,“用加密貨幣支付”按鈕可以極大地推動銷售。
本文演示如何在不依賴第三方服務(wù)的情況下將自定義、安全的加密支付方法集成到任何在線商店中。編碼和維護智能合同需要相當繁重的工作,我們正在把這項工作移交給塊環(huán)鏈建設(shè)者常用的工具鏈Truffle套件。為了在開發(fā)期間和應(yīng)用程序后端提供對區(qū)塊鏈節(jié)點的訪問,我們依賴于 Infura 節(jié)點,這些節(jié)點以慷慨的免費層提供對以太網(wǎng)絡(luò)的訪問。一起使用這些工具將使開發(fā)過程更加容易。
場景: Amethon 書店
我們的目標是為可下載的電子書建立一個店面,接受以太區(qū)塊鏈的本地貨幣(“以太”)和 ERC20穩(wěn)定幣(以美元掛鉤的支付令牌)作為一種支付方法。從現(xiàn)在開始,我們稱之為“ Amethon”。完整的實現(xiàn)可以在附帶的 GitHub monorepo 中找到。所有的代碼都用Typescript 編寫 并且能夠被yarn build 或yarn dev 命令編譯。
我們將一步一步地指導(dǎo)您完成這個過程,但是熟悉智能契約、以太坊以及 Solidy 編程語言的最低知識可能有助于您一起閱讀。我們建議您首先閱讀一些基礎(chǔ)知識,以熟悉生態(tài)系統(tǒng)的基本概念。
Application Structure
存儲后端是作為一個 CRUD API 構(gòu)建的,它本身不連接到任何區(qū)塊鏈。它的前端觸發(fā)對該 API 的支付請求,客戶使用他們的加密錢包完成。
Amethon 被設(shè)計成一個“傳統(tǒng)的”電子商務(wù)應(yīng)用程序,它負責業(yè)務(wù)邏輯,除了支付本身之外不依賴任何上鏈數(shù)據(jù)。在結(jié)帳過程中,后端發(fā)出 PaymentRequest 對象,這些對象攜帶一個唯一標識符(例如“發(fā)票號碼”) ,用戶將其附加到支付交易中。
后臺守護進程偵聽各自的契約事件,并在檢測到付款時更新商店的數(shù)據(jù)庫。
Amethon 的支付結(jié)算
收款人合約
在 Amethon 中心,PaymentReceiversmart 合同代表店面所有者接受和托管付款。
每次用戶向 PaymentReceiver 合同發(fā)送資金時,都會發(fā)送一個 PaymentReceivedevent,其中包含有關(guān)支付的來源(客戶的 Etherum 帳戶)、其總價值、所使用的 ERC20令牌合同地址以及指向后端數(shù)據(jù)庫條目的 paymentID 的信息。
TypeScript-JSX
event PaymentReceived( address indexed buyer, uint256 value, address token, bytes32 paymentId );
以太合同的作用類似于基于用戶(即“外部擁有”/EOA)的帳戶,并在部署時獲得自己的帳戶地址。接收本地以太貨幣需要實現(xiàn)接收和回退功能,這些功能在某人將以太資金轉(zhuǎn)移到合同時被調(diào)用,并且沒有其他功能簽名與調(diào)用匹配:
TypeScript-JSX
receive() external payable { emit PaymentReceived(msg.sender, msg.value, ETH_ADDRESS, bytes32(0)); } fallback() external payable { emit PaymentReceived( msg.sender, msg.value, ETH_ADDRESS, bytes32(msg.data)); }
官方的 Soliity 文檔指出了這些函數(shù)之間的細微差別: 當傳入的事務(wù)不包含額外數(shù)據(jù)時,就會調(diào)用 Receiveis,否則就會調(diào)用回退。以太本身的本地貨幣不是 ERC20令牌,除了作為計數(shù)單位外沒有其他用途。然而,它有一個可識別的地址(0xEeeeeEeeEeeEeeEeeEeeEeeEeeEeeeeeeeeeeEEEE) ,我們使用信號以太支付在我們的 PaymentReceivedevents。
然而,以太傳輸有一個主要的缺點: 接收時允許的計算量非常低。客戶送來的天然氣只能讓我們發(fā)出一個事件,但不能將資金重定向到店主的原始地址。因此,接收者契約保留所有傳入的以太,并允許商店所有者在任何時候?qū)⑺鼈冡尫诺阶约旱膸糁?
TypeScript-JSX
function getBalance() public view returns (uint256) { return address(this).balance;}function release() external onlyOwner { (bool ok, ) = _owner.call{value: getBalance()}(“”); require(ok, “Failed to release Eth”);}
由于歷史原因,接受 ERC20令牌作為支付稍微有點困難。在2015年,最初規(guī)范的作者無法預(yù)測即將到來的需求,因此使 ERC20標準的接口盡可能簡單。最值得注意的是,ERC20合同不能保證通知收件人有關(guān)轉(zhuǎn)移的信息,所以當 ERC20令牌被轉(zhuǎn)移到我們的 PaymentReceiver 時,它無法執(zhí)行代碼。
ERC20生態(tài)系統(tǒng)已經(jīng)發(fā)生了演變,現(xiàn)在包括額外的規(guī)格。例如,EIP1363標準解決了這個問題。不幸的是,你不能依靠主要的穩(wěn)定幣平臺來實現(xiàn)它。
因此,Amethon 必須以“經(jīng)典”的方式接受 ERC20象征性付款。契約不會在不知情的情況下“丟棄”令牌,而是代表客戶負責傳輸。這就要求用戶首先允許合同處理一定數(shù)額的資金。這不方便地要求用戶在與實際支付方法交互之前首先將 Approval 事務(wù)傳輸?shù)?ERC20令牌合同。EIP-2612可能會改善這種情況,但是,我們必須按照舊的規(guī)則玩暫時。
TypeScript-JSX
function payWithErc20( IERC20 erc20, uint256 amount, uint256 paymentId ) external { erc20.transferFrom(msg.sender, _owner, amount); emit PaymentReceived( msg.sender, amount, address(erc20), bytes32(paymentId)
編譯、部署和可變安全性
有幾個工具鏈允許開發(fā)人員編譯、部署和與 Etherum 智能契約進行交互,但最先進的工具鏈之一是 Truffle Suite。它帶有一個基于 Ganache 的內(nèi)置開發(fā)塊鏈和一個遷移概念,允許您自動化并安全地運行契約部署。
在“真正的”區(qū)塊鏈基礎(chǔ)設(shè)施上部署合同,比如以太網(wǎng)測試網(wǎng),需要兩件事: 一個連接到區(qū)塊鏈節(jié)點的以太網(wǎng)提供商和一個賬戶的私鑰/錢包助記符或者一個可以代表一個賬戶簽署交易的錢包連接。該帳戶還需要有一些(測試網(wǎng))以太在它支付天然氣費用期間部署。
MetaMask 就是干這個的。創(chuàng)建一個新的帳戶,你不會使用其他任何東西,但部署(它將成為“所有者”的合同) ,并與一些以太使用你首選的測試網(wǎng)的水龍頭(我們推薦 Paradigm)。通常情況下,你現(xiàn)在可以導(dǎo)出賬戶的私鑰(“ Account Details”> “ Export Private Key”)并將其與你的開發(fā)環(huán)境連接起來,但是為了規(guī)避這個工作流程所隱含的所有安全問題,Truffle 提供了一個專用的儀表板網(wǎng)絡(luò)和網(wǎng)絡(luò)應(yīng)用程序,可以用來在瀏覽器中使用 Metamask 簽署合同部署之類的事務(wù)。要啟動它,在一個新的終端窗口中執(zhí)行 truffle dashboard,并使用一個帶有活動 Metamask 擴展的瀏覽器訪問 http://localhost:24012/。
使用 truffle 的儀表板在不暴露私鑰的情況下對事務(wù)進行簽名
Amethon 項目還依賴于各種秘密設(shè)置。注意,由于 dotenv-flow 的工作方式,。Envfiles 包含示例或公開可見的設(shè)置,這些設(shè)置被 gitignored.env.localfiles 覆蓋。收到。并覆蓋它們的值。
若要將本地環(huán)境連接到以太網(wǎng)絡(luò),請訪問同步塊鏈節(jié)點。當然,你可以下載其中一個客戶端,然后等待它在你的機器上同步,但是將你的應(yīng)用程序連接到 Ethereum 的節(jié)點上要方便得多,這些節(jié)點是以服務(wù)的形式提供的,其中最著名的是 Infura。他們的免費層為您提供三種不同的訪問密鑰和每月10萬的 RPC 請求,支持范圍廣泛的以太網(wǎng)絡(luò)。
注冊之后,注意你的 INFURA 密鑰并把它放在你的合同中. env.localas INFURA _ KEY。
如果你想與合同互動,例如在 Kovan 網(wǎng)絡(luò)上,只需在所有的 truffle 命令中添加相應(yīng)的 truffle 配置和 -network Kovan 選項。您甚至可以啟動一個交互式控制臺: 紗松露控制臺——網(wǎng)絡(luò) Kovan。在本地測試契約不需要任何特殊的設(shè)置過程。為了讓我們的生活變得簡單,我們使用 Metamask 通過truffle儀表板提供者注入的提供者和簽名者。
改變?yōu)閏ontracts 文件夾 并運行yarn truffle develop.這將啟動一個本地區(qū)塊鏈與預(yù)先資助的帳戶,并打開一個連接的控制臺上。要將你的 Metamask 錢包連接到開發(fā)網(wǎng)絡(luò),使用 http://localhost:9545作為其 RPC 端點創(chuàng)建一個新的網(wǎng)絡(luò)。當鏈條開始時,請注意列出的帳戶: 您可以導(dǎo)入他們的私人密鑰到您的 Metamask 錢包發(fā)送交易代表您的本地區(qū)塊鏈。
輸入compile來一次編譯所有契約,并使用migrate將它們部署到本地鏈中。 你可以通過請求它們當前部署的實例來與契約交互,并像這樣調(diào)用它的函數(shù):
TypeScript-JSX
pr = await PaymentReceiver.deployed()balance = await pr.getBalance()
一旦您對結(jié)果感到滿意,您就可以將它們部署到公共測試網(wǎng)(或 mainnet)上,以及:
Shell
yarn truffle migrate –interactive –network dashboard
The Backend
存儲 API/CRUD
我們的后端提供了一個 JSON API 來與高層次的支付實體進行交互。我們決定使用 TypeORM 和本地 SQLite 數(shù)據(jù)庫來支持 Books 和 PaymentRequest 的實體。書籍代表我們商店的主要實體,有一個零售價格,以美分表示。為了最初在數(shù)據(jù)庫中添加書籍,可以使用 companyingSeed.ts 文件。編譯完文件后,可以通過 invokingnode build/Seed.js 執(zhí)行它。
TypeScript
//backend/src/entities/Book.tsimport { Entity, Column, PrimaryColumn, OneToMany } from “typeorm”;import { PaymentRequest } from “./PaymentRequest”;@Entity()export class Book { @PrimaryColumn() ISBN: string; @Column() title: string;
注意: 在任何計算機系統(tǒng)上都強烈建議不要將貨幣值存儲為浮動值,因為對浮動值進行操作肯定會引入精度錯誤。這也是為什么所有的加密令牌都使用18位十進制數(shù),而 Soliity 甚至沒有 float 數(shù)據(jù)類型的原因。1以太實際上代表“10000000000000000000”為最小的以太單位。
對于打算從 Amethon 購買書籍的用戶,首先通過調(diào)用/books/: isbn/orderpath 為他們的商品創(chuàng)建一個個人支付請求。這將創(chuàng)建一個新的唯一標識符,必須隨每個請求一起發(fā)送。
我們在這里使用的是普通整數(shù),但是,對于實際的用例,您將使用更復(fù)雜的東西。唯一的限制是 id 的二進制長度必須適合32字節(jié)(uint256)。EachPaymentRequest 繼承書籍的零售價值(以美分為單位) ,并記錄客戶的地址,在購買過程中確定 Hash 和 paidUSDCentwill。
TypeScript
//backend/src/entities/PaymentRequest.ts@Entity()export class PaymentRequest { @PrimaryGeneratedColumn() id: number; @Column(“varchar”, { nullable: true }) fulfilledHash: string | null; @Column() address: string; @Column() priceInUSDCent: number; @Column(“mediumint”, { nullable: true }) paidUSDCent: number; @ManyToOne(() => Book, (book) => book.payments) book: Book;}
創(chuàng)建 PaymentRequesttity 的初始訂單請求如下:
TypeScript
POST http://localhost:3001/books/978-0060850524/orderContent-Type: application/json{ “address”: “0xceeca1AFA5FfF2Fe43ebE1F5b82ca9Deb6DE3E42”}—>{ “paymentRequest”: { “book”: { “ISBN”: “978-0060850524”, “title”: “Brave New World”, “retailUSDCent”: 1034 }, “address”: “0xceeca1AFA5FfF2Fe43ebE1F5b82ca9Deb6DE3E42”, “priceInUSDCent”: 1034, “fulfilledHash”: null, “paidUSDCent”: null, “id”: 6 }, “receiver”: “0x7A08b6002bec4B52907B4Ac26f321Dfe279B63E9”}
區(qū)塊鏈監(jiān)聽器后臺服務(wù)
查詢區(qū)塊鏈的狀態(tài)樹不會花費客戶機任何氣體,但節(jié)點仍然需要計算。當這些操作變得計算量太大時,它們可能會超時。對于實時交互,強烈建議不要輪詢鏈狀態(tài),而是監(jiān)聽事務(wù)發(fā)出的事件。這需要使用支持 WebSocket 的提供程序,因此一定要使用以 wss://as URL 方案開頭的 Infura 端點,用于后端的 PROVIDER _ RPC 環(huán)境變量。然后,您可以啟動后端的 daemon.tsscript 并在任何鏈上偵聽 PaymentReceivedevents:
TypeScript
//backend/src/daemon.ts const web3 = new Web3(process.env.PROVIDER_RPC as string); const paymentReceiver = new web3.eth.Contract( paymentReceiverAbi as AbiItem[], process.env.PAYMENT_RECEIVER_CONTRACT as string ); const emitter = paymentReceiver.events.PaymentReceived({ fromBlock: “0”, });
注意我們是如何用一個應(yīng)用二進制接口實例化契約實例的。Soliity 編譯器生成 ABI,并為 RPC 客戶機提供有關(guān)如何編碼事務(wù)以調(diào)用和解碼智能契約上的函數(shù)、事件或參數(shù)的信息。
一旦實例化,您就可以在契約的 PaymentRecected 日志(從塊0開始)上鉤住一個偵聽器,并在收到后處理它們。
因為 Amethon 支持 Ether 和 stablecoin (“ US”)支付,所以守護進程的 handlePaymentEventmethod 首先檢查用戶支付中使用了哪個令牌,并在需要時計算其美元價值:
TypeScript
//backend/src/daemon.tsconst ETH_USD_CENT = 2_200 * 100;const ACCEPTED_USD_TOKENS = (process.env.STABLECOINS as string).split(“,”);const NATIVE_ETH = “0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE”;const handlePaymentEvent = async (event: PaymentReceivedEvent) => { const args = event.returnValues; const paymentId = web3.utils.hexToNumber(args.paymentId); const decimalValue = web3.utils.fromWei(args.value); const payment = await paymentRepo.findOne({ where: { id: paymentId } }); let valInUSDCents; if (args.token === NATIVE_ETH) { valInUSDCents = parseFloat(decimalValue) * ETH_USD_CENT; } else { if (!ACCEPTED_USD_TOKENS.includes(args.token)) { return console.error(“payments of that token are not supported”); } valInUSDCents = parseFloat(decimalValue) * 100; } if (valInUSDCents < payment.priceInUSDCent) { return console.error(`payment [${paymentId}] not sufficient`); } payment.paidUSDCent = valInUSDCents; payment.fulfilledHash = event.transactionHash; await paymentRepo.save(payment);};
The Frontend
我們書店的前端是建立在官方的創(chuàng)建反應(yīng)應(yīng)用程序模板與類型支持,并使用 Tailwind 的基本樣式。它支持所有已知的 CRA 腳本,因此您可以在創(chuàng)建自己的腳本之后通過紗線啟動本地腳本。本地文件,包含之前創(chuàng)建的支付接收方和 stablecoin 合同地址。
注意: CRA5將他們的 webpack 依賴關(guān)系轉(zhuǎn)移到了一個不再支持瀏覽器中節(jié)點填充的版本。這打破了今天幾乎所有與以太坊相關(guān)的項目的構(gòu)建。避免彈出的一個常見解決方案是掛鉤到 CRA 構(gòu)建過程中。我們正在使用 response-app-rewire,但是你可以簡單地呆在 CRA4,直到社區(qū)提出一個更好的解決方案。
連接 WEB3錢包
任何 Dapp 的關(guān)鍵部分都是連接到用戶的錢包。您可以嘗試按照正式的 MetaMask 文檔手動連接該進程,但我們強烈建議使用適當?shù)?React 庫。我們發(fā)現(xiàn) Noah Zinsmeister 的 web3反應(yīng)是最好的。檢測和連接 web3客戶端歸結(jié)為以下代碼(ConnectButton.tsx) :
TypeScript
//frontend/src/components/ConnectButton.tsimport { useWeb3React } from “@web3-react/core”;import { InjectedConnector } from “@web3-react/injected-connector”;import React from “react”;import Web3 from “web3”;export const injectedConnector = new InjectedConnector({ supportedChainIds: [42, 1337, 31337], //Kovan, Truffle, Hardhat});export const ConnectButton = () => { const { activate, account, active } = useWeb3React(); const connect = () => { activate(injectedConnector, console.error); }; return active ? ( connected as: {account} ) : ( Connect );};
通過將應(yīng)用程序的代碼封裝在 上下文中,您可以從任何組件使用 useWeb3Reacthook 訪問 web3提供程序、帳戶和連接狀態(tài)。由于 Web3React 與所使用的 web3庫(Web3.js 或 ethers.js)是不可知的,因此您必須提供一個回調(diào),以生成一個連接的“庫”:
TypeScript-JSX
//frontend/src/App.tsximport Web3 from “web3”;function getWeb3Library(provider: any) { return new Web3(provider);}
支付流程
從 Amethon 后端加載可用的圖書后, 組件首先檢查該用戶的付款是否已經(jīng)處理,然后顯示打包在 組件中的所有支持的付款選項。
Paying With ETH
負責啟動對 PaymentReceiveragreement 的直接以太傳輸。由于這些調(diào)用并不直接與契約接口交互,我們甚至不需要初始化契約實例:
TypeScript-JSX
//frontend/src/components/PayButton.tsxconst weiPrice = usdInEth(paymentRequest.priceInUSDCent);const tx = web3.eth.sendTransaction({ from: account, //the current user to: paymentRequest.receiver.options.address, //the PaymentReceiver contract address value: weiPrice, //the eth price in wei (10**18) data: paymentRequest.idUint256, //the paymentRequest’s id, converted to a uint256 hex string});const receipt = await tx;onConfirmed(receipt);
如前所述,由于新的事務(wù)帶有 msg.data 字段,Soliity 的約定觸發(fā) PaymentReceiver 的回退()外部支付函數(shù),該函數(shù)發(fā)出一個帶有 Ether 令牌地址的 PaymentReceivedevent。這由被守護的鏈監(jiān)聽器接收,該監(jiān)聽器相應(yīng)地更新后端的數(shù)據(jù)庫狀態(tài)。
靜態(tài)助手函數(shù)負責將當前的美元價格轉(zhuǎn)換為以太值。在一個真實的場景中,從值得信賴的第三方(如 Coingecko)或像 Uniswap 這樣的 DEX 查詢匯率。這樣做允許您擴展 Amethon 以接受任意令牌作為支付。
TypeScript
//frontend/src/modules/index.tsconst ETH_USD_CENT = 2_200 * 100;export const usdInEth = (usdCent: number) => { const eth = (usdCent / ETH_USD_CENT).toString(); const wei = Web3.utils.toWei(eth, “ether”); return wei;};
用 ERC20穩(wěn)定幣支付
由于前面提到的原因,從用戶的角度來看,ERC20令牌中的支付稍微復(fù)雜一些,因為不能簡單地刪除契約中的令牌。像幾乎所有具有類似用例的人一樣,我們必須首先請求用戶允許我們的 PaymentReceiver 合同轉(zhuǎn)移他們的資金,并調(diào)用代表用戶轉(zhuǎn)移請求資金的實際 payWithEerc20方法。
下面是 PayWithStableButton 對選定的 ERC20令牌授予權(quán)限的代碼:
TypeScript
//frontend/src/components/PayWithStableButton.tsxconst contract = new web3.eth.Contract( IERC20ABI as AbiItem[], process.env.REACT_APP_STABLECOINS);const appr = await coin.methods .approve( paymentRequest.receiver.options.address, //receiver contract’s address price // USD value in wei precision (1$ = 10^18wei) ) .send({ from: account, });
請注意,設(shè)置 ERC20令牌的契約實例所需的 ABI 接收一般的 IERC20 ABI。我們使用從 OpenZeppelin 的官方庫中生成的 ABI,但是任何其他生成的 ABI 都可以完成這項工作。在批準轉(zhuǎn)讓后,我們可以開始付款:
TypeScript-JSX
//frontend/src/components/PayWithStableButton.tsxconst contract = new web3.eth.Contract( PaymentReceiverAbi as AbiItem[], paymentRequest.receiver.options.address);const tx = await contract.methods .payWithErc20( process.env.REACT_APP_STABLECOINS, //identifies the ERC20 contract weiPrice, //price in USD (it’s a stablecoin) paymentRequest.idUint256 //the paymentRequest’s id as uint256 )
簽署下載請求
最后,我們的客戶可以下載他們的電子書。但是有一個問題: 既然我們沒有“登錄”用戶,我們?nèi)绾未_保只有真正為內(nèi)容付費的用戶才能調(diào)用我們的下載路徑?答案是加密簽名。在將用戶重定向到我們的后端之前, 組件允許用戶簽署一個獨特的消息,該消息被提交作為帳戶控制的證明:
TypeScript-JSX
//frontend/src/components/DownloadButton.tsxconst download = async () => { const url = `${process.env.REACT_APP_BOOK_SERVER}/books/${book.ISBN}/download`; const nonce = Web3.utils.randomHex(32); const dataToSign = Web3.utils.keccak256(`${account}${book.ISBN}${nonce}`); const signature = await web3.eth.personal.sign(dataToSign, account, “”); const resp = await ( await axios.post(
后端的下載路由可以恢復(fù)簽名者的地址,方法是按照與用戶以前相同的方式組合消息,并使用消息和提供的簽名調(diào)用加密套件的 ecRecovery 方法。如果恢復(fù)的地址與我們數(shù)據(jù)庫中已完成的 PaymentRequest 匹配,我們知道我們可以允許訪問請求的電子書資源:
TypeScript
//backend/src/server.tsapp.post( “/books/:isbn/download”, async (req: DownloadBookRequest, res: Response) => { const { signature, address, nonce } = req.body; //rebuild the message the user created on their frontend const signedMessage = Web3.utils.keccak256( `${address}${req.params.isbn}${nonce}` );
這里提供的帳戶所有權(quán)證明仍然不是完全正確的。任何知道所購物品的有效簽名的人都可以成功地調(diào)用下載路由。最后的修復(fù)方法是首先在后端上創(chuàng)建隨機消息,然后讓客戶簽名并批準它。由于用戶無法理解他們應(yīng)該簽署的混亂的十六進制代碼,他們不會知道我們是否會欺騙他們簽署另一個有效的交易,這可能會危及他們的帳戶。
雖然我們已經(jīng)通過使用 web3的 eth.Personal.signmethod 避免了這種攻擊向量,但是最好以人性化的方式顯示要簽名的消息。這就是 EIP-712達到的目標ーー MetaMask 已經(jīng)支持這一標準。
結(jié)論及下一步
對于開發(fā)者來說,接受電子商務(wù)網(wǎng)站的付款從來都不是一件容易的事情。盡管 web3生態(tài)系統(tǒng)允許店面接受數(shù)字貨幣,但獨立于服務(wù)的插件解決方案的可用性不足。本文演示了一種安全、簡單和自定義的方法來請求和接收加密支付。
還有進一步發(fā)展的空間。以太網(wǎng)上 ERC20傳輸?shù)奶烊粴獬杀具h遠超過我們的賬面價格。低價物品的加密支付在天然氣友好的環(huán)境中是有意義的,比如 Gnosis Chain (他們的“本地”以太貨幣是 DAI,所以你甚至不用擔心穩(wěn)定幣的轉(zhuǎn)移)或者 Arbitrum。您還可以使用購物車簽出來擴展后端,或者使用 DEXes 將任何傳入的 ERC20令牌交換到您首選的貨幣。
畢竟,web3的承諾是允許沒有中間商的直接貨幣交易,并為希望吸引懂加密技術(shù)的客戶的在線商店增加重大價值。