Paying

Overview

When an asset is for sale, the first buyer to provide the required amount, in the required currency, will acquire the asset.

As usual, the blockchain determines the order of arrival of payments. In case with race conditions, where multiple transactions for the very same BuyNowId arrive, only the first payment is correctly processed, and the rest simply fail.

Payments in cryptocurrency are performed by directly transferring funds to a layer-1 smart contract. There are two types of almost identical such contracts:

  • prepared to accept only the native cryptocurrency of a layer-1 blockchain,

  • prepared to accept ERC20 tokens deployed in of a layer-1 blockchain.

The flow is almost identical for both. The only minor difference is that, in the ERC20 case, every buyer needs to first approve the escrow contract to manage his/her ERC20 tokens. This needs to be done only once in a buyer's lifetime.

Payments Gateway

Living Assets payments can be integrated by either implementing the API calls described in the sections below, or by simply redirecting users to the Living Assets Payments Gateway.

For a comprehensive understanding of the payment gateway URL's anatomy, please refer to Payments Gateway section.

To obtain fake card numbers that can be used for testing purposes within the Payment Gateway, click on one of these links:

Payment Request

The first step to pay for an asset in BuyNow mode is to execute this request:

mutation { 
  createBuyNowPayment(
    input: { 
      buyNowId,
      buyerId,
    }
  )
}

Check the paying for a BuyNow example for more info.

This mutation returns the following data:

  PaymentOutput {
      paymentUrl,
      paymentId,
      price,
      feeBPS,
      universeId,
      deadline,
      buyerId,
      seller,
      sellerSig,
  }

which is needed in the next step.

Payment TX to the Blockchain

To complete the payment, the buyer just needs to provide the funds, alongside the data from the previous query, to the blockchain smart contract.

The pay method in the corresponding class of the freeverse-marketsigner-js library may be used; there is one class for payments in native crypto, and one for ERC20 tokens:

const { NativeCryptoPayments } = require('freeverse-marketsigner-js');

Checkout examples in both types of currency:

The input parameters provided to the buyNow method,

paymentsInstance.buyNow({
  paymentData,
  operatorSignature,
  sellerSignature,
  from: buyerAddr
})

comprise:

const paymentData =
  {
      paymentId: PaymentOutput.paymentId,
      amount: PaymentOutput.price.toString(),
      feeBPS: PaymentOutput.feeBPS,
      universeId: PaymentOutput.universeId,
      deadline: PaymentOutput.deadline,
      buyer: PaymentOutput.buyerId,
      seller: PaymentOutput.seller,
  }

const operatorSignature = '0x' + PaymentOutput.paymentUrl
const sellerSignature = '0x' + PaymentOutput.sellerSig

As always, make sure to check for enough confirmation blocks before considering the TX confirmed.

Right after the payment is confirmed, the BuyNow is moved to the state ASSET_TRANSFERING. Then, after the layer-2 is synchronized with the layer-1, which happens at most within 15 minutes, the asset transfer is settled, and the BuyNow state moves to WITHDRAWABLE_BY_SELLER.

In ERC20 payments, make sure the buyer has approved the escrow contract for managing his/her tokens, as mandatory by the ERC20 standard. The approve or approveInfinite methods provided in paymentsInstance may be used for this purpose.

Seller withdrawal

As before, start with a request:

mutation {
  cashoutCrypto(
    input: {signature: "${signature}", paymentId: "${paymentId}"}
  ) {
    signature
    paymentId
    assetTransferSuccess
  }
}

The result of the mutation cashoutCrypto is:

type FinalizePaymentOutput {
    signature,
    paymentId,
    assetTransferSuccess
}

which can be used by the seller to call the finalizeAndWithdraw method from the corresponding class:

paymentsInstance.finalizeAndWithdraw(
  { assetTransferData, signature, from: sellerAddr },
)

where:

const assetTransferData = 
{
    paymentId: FinalizePaymentOutput.paymentId,
    wasSuccessful: FinalizePaymentOutput.assetTransferSuccess,
}
const signature = '0x' + FinalizePaymentOutput.signature

Check out the examples for withdrawals in native crypto and erc20 for more info.

When the TX is confirmed, the BuyNow state moves to SUCCESS.

Buyer refunds

In the unlikely event that an asset transfer fails, the smart contract is prepared to refund the buyer. The BuyNow will be moved to state WITHDRAWABLE_BY_BUYER, and the refund takes place analogously to the withdrawal process explained above.

The cashoutCrypto mutation will return assetTransferSuccess = false, and the refund proceeds with the very same call to the finalizeAndWithdraw method as in the withdrawal case, but with such false value of assetTransferSuccess. After a successful refund, the BuyNow will move to the state REFUNDED.

Last updated

freeverse.io