Building a Non-Custodial Payment Gateway on the Pop Network
Building a Non-Custodial Payment Gateway on the Pop Network

Table of Contents

In part one, we explored building a non-custodial payment gateway on Moonbeam. In this article, we’ll dive into an alternative approach using the Pop Network, leveraging its advanced Wasm and XCM capabilities.

🎥 Watch the Video Overview: For a visual walkthrough of the implementation, check out our YouTube video: Building a Non-Custodial Stablecoin Payment Gateway on the Pop network.

Example Use Case

Imagine a merchant selling online products who wishes to accept payments in USDC. The customer pays in USDC, which is routed to the merchant securely. This article explores how this use case is implemented on the Pop Network.

The Pop Network Solution

This implementation is part of KwickBit’s project funded by the Decentralized Futures Grant from the Web3 Foundation, showcasing innovative solutions for non-custodial payment gateways.

User Flow Logic

1. Transfer PAS Tokens:

  • The user teleports PAS tokens from the relay chain to the Pop Network.

2. Call the Payment Smart Contract:

  • The user interacts with the pay method on the smart contract, specifying payment details.

3. Off-Chain Indexer and Callback:

  • An off-chain indexer listens for events emitted by the Pop network and calls back the smart contract to finalize the payment process.

Implementation Logic with Indexer

  • The Pop Network implementation uses an off-chain indexer to monitor `MessageQueue.Processed` events.
  • The indexer ensures payment statuses are updated and triggers a callback to the smart contract to confirm success or failure.

Key Steps:

1. Deploy the smart contract to the Pop Network.

2. Use an indexer to process XCM-related events and finalize payments.

Smart Contract Highlights (Ink!)

The following piece of code is a critical part of the `pay` method implementation. It ensures the payment flow is initiated correctly, handles the XCM message construction, and updates the payment status:

// Check if payment_id is already going on
let payment_status_opt = self.payments.get(payment_id.to_vec());
assert_ne!(payment_status_opt, Some(PAYMENT_ONGOING), "Payment is on going already");
assert_ne!(payment_status_opt, Some(PAYMENT_DONE), "Payment is already completed");

let ah = Junctions::from([Parachain(1000)]);
let destination: Location = Location { parents: 1, interior: ah};
let asset: Asset = (Location::parent(), FEE_MAX).into();
let sc_ah_address = Self::get_ah_address();
let sc_location: Location = AccountId32{network: None, id: sc_ah_address}.into();

let transfer_approved = RuntimeCall::Assets(AssetsCall::TransferApproved{
id: ASSET_ID,
owner: MultiAddress::<AccountId, ()>::Id(self.env().caller()),
destination: MultiAddress::<AccountId, ()>::Id(AccountId::from(sc_ah_address)),
amount
});

let message: Xcm<()> = Xcm::builder()
.withdraw_asset(asset.clone().into())
.buy_execution(asset, Unlimited)
.set_appendix(
Xcm::builder_unsafe()
.refund_surplus()
.deposit_asset(
Wild(WildAsset::All),
sc_location.clone(),
)
.build(),
)
.set_error_handler(Xcm::builder_unsafe().report_error(QueryResponseInfo{
destination: destination.clone(),
query_id: u64::from_le_bytes(payment_id[0..8].try_into().unwrap()),
max_weight: Weight::from_parts(REF_TIME, PROOF_SIZE)
}).build())
.transact(
OriginKind::SovereignAccount,
Weight::from_parts(REF_TIME, PROOF_SIZE),
transfer_approved.encode().into(),
)
.expect_transact_status(MaybeErrorCode::Success)
.set_topic(payment_id)
.build();

let hash = self.env().xcm_send(
&VersionedLocation::V4(destination),
&VersionedXcm::V4(message),
)?;

self.payments.insert(payment_id.to_vec(), &PAYMENT_ONGOING);

Ok(hash)

Explaining the XCM Message

The XCM message in this implementation is crafted to facilitate a cross-chain transfer of USDC and handle the complexities of multi-chain interactions. Here’s a step-by-step breakdown:

1. Payment Validation: The method begins by validating the `payment_id` to ensure that no duplicate or ongoing payments are processed.

2. Set Appendix: This part of the XCM message ensures that any surplus from the operation is refunded and that assets are deposited into a specific account (`sc_location`).

The appendix includes:

  • Refund Surplus: Ensures that any unused portion of the allocated fee is returned to the sender.
  • Deposit Asset: Deposits any received assets to the smart contract address on the Asset Hub.

3. Transact: This instruction encapsulates a call to the Asset Hub’s smart contract to perform the `transferApproved` operation. This transfers the payment amount from the payer’s account to the smart contract address on the Asset Hub.

Key parameters include:

  • OriginKind: Specifies the origin of the transaction as a Sovereign Account.
  • Weight: Allocates a specific weight for executing the transaction.
  • Encoded Call Data: Encodes the call to `transferApproved`, including payment details such as the amount, payer, and destination address.

4. Error Handling: The XCM message includes error-handling logic to handle potential failures. It reports errors using the `report_error` instruction and tracks the issue using a query ID derived from the `payment_id`.

5. Set Topic: The topic associates the XCM message with the specific `payment_id`, enabling tracking and correlation with off-chain events.

6. Finalize Payment Status: The status of the payment is updated to `PAYMENT_ONGOING` once the message is sent, ensuring no duplicate processing.

Additional Details:

– Two methods, `callback_success` and `callback_fail`, are invoked by a process listening to the indexer to finalize the payment process based on the outcome of the XCM message.

– The actual transfer to the merchant is performed through the smart contract method `generic_execute_ah`, which is encapsulated in an XCM `transact` message. This method uses the `transferApproved` operation to securely route funds to the merchant.

  • Unlike the Moonbeam solution, this implementation does not handle JSON data signatures, as the focus of this proof of concept (POC) is on mastering the complexities of XCM integration, which is the most challenging aspect of this benchmark.

Links

Check our article on Medium: https://medium.com/kwickbit/building-a-non-custodial-payment-gateway-on-the-pop-network-42a2ac14c6a0

Share the Post:
KwickBit logo, representing the non-custodial crypto payment gateway for eCommerce and websites.