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

Coming very soon, clients will have the option to simply redirect their users to a Living Assets Payments Gateway that will deal with the payments process, as if they were interacting with a standard payments provider gateway. This gateway provides a default, always up to date, implementation of the steps described below.

Continue reading in order to implement the payments process directly in your application.

Payment Request

To pay for an existing BuyNow, first execute a request:

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

Check the paying for a BuyNow example for more info.

Note

The buyer's signature will cease to be required after the next platform upgrade.

This mutation returns the following data:

type PaymentOutput {
    paymentUrl,
    paymentId,
    price,
    feeBPS,
    universeId,
    deadline,
    buyerId,
    seller
}
which is needed in the next step.

Payment TX to the Blockchain

To complete the payment, just 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 data provided to the pay method,

paymentsInstance.pay({ paymentData, signature, from: buyerAddr })
is:
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 signature = 0x + PaymentOutput.paymentUrl

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.