Skip to content

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.

The gateway fully deals with the payment process, in complete analogy to how users normally interact with standard payments gateways. It provides an always up-to-date, neutral-looking, implementation of the payments API.

Users may be simply redirected to:

https://paymentgw.staging.blackhole.gorengine.com/payment/{{BuyNowId}}/{{BuyerWeb3Address}}/{{RedirectURLbase64}}
where:

  • {{BuyNowId}} is the BuyNowId assigned when putting an asset for sale;

  • {{BuyerWeb3Address}} is the public web3 address of the user performing the purchase;

  • {{RedirectURLbase64}} is the base64-encoded URL where the user will be redirected after finishing the purchase.

An example would be:

https://paymentgw.staging.blackhole.gorengine.com/payment/0xe0a6d1077c66bd459c67f84ee372318b935a9dec1385f66b6b0f15e0a405b7ea/0x0d906584Bb21B06895F40E8F31C80c3B6e251486/aHR0cHM6Ly9tYXJrZXQuc3RhZ2luZy5ibGFja2hvbGUuZ29yZW5naW5lLmNvbS9hc3NldHM=

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');
const { ERC20Payments } = 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

Note

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.

Note

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 {
  cashout(
    input: {signature: "${signature}", paymentId: "${paymentId}", email: "${email}", bankAccountNumber: ""}
  ) {
    signature
    paymentId
    assetTransferSuccess
  }
}

Note

The bank account number should be empty when requesting a crypto cashout. This argument will be removed in future versions.

The result of the mutation cashout 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 move to state WITHDRAWABLE_BY_BUYER, and the refund takes place analogously to the withdraw process explained above.

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