Remittance Advice

In this example we take you through how to work with Remittance Advices using both GraphQL and Webhooks.

What you’ll learn

  • Key concepts and terms
  • Key object relationships
  • How to work with GraphQL and Webhooks
  • Further financial information available

Key Terms

Before launching in to the the use of the API and Webhooks related to remittance advices, it’s worth covering some of the key concepts we’ll be working with:

TermDescription
OperatorThe business entity that runs the marketplace and is the merchant of record for the transactions that occur on the marketplace. The GraphQL features described in this article can only be employed by Operators.
SellerBusiness entity that places products for sale on the marketplace. A Seller is responsible for fulfilling Invoices on an Order, a Seller can only have 1 Invoice per Order. The seller will be remitted (paid) by the Operator when they fulfil their invoices.
OrderAn Order is the “marketplace-level” object that acts as the container for a customer’s entire purchase. An Order object in Marketplacer will always have at least 1 Invoice attached to it, (1 invoice per seller)
InvoiceAn Invoice is the part of the overall customer order that an individual Seller has to fulfil. An Order will always have at least 1 Invoice (1 per Seller participating in the Order)
Invoice AmendmentFollowing the completion of an Order (and therefore the creation of Invoices) there may be reasons why those individual Invoices need to be amended, (for example when a customer requires a refund). In such cases an Invoice will have associated Invoice Amendments. An Invoice can have 0 or more Invoice Amendments.
RemittanceRemittance is both a Marketplacer workflow and a GraphQL object type. From a workflow perspective it is used to ensure that Invoices have been adequately fulfilled, and that Sellers should then be paid (remitted).
Remittance AdviceRemittance Advices group multiple Remittances together under 1 “advice”. Prior to this feature you had to rely solely on the relationship between a Remittance and an Invoice / Invoice Amendment (a Remittance can only relate to 1 Invoice or Invoice Amendment) which made things like reporting problematic. With the release of Remittance Advices, it is now easy to see the relationships between multiple Remittances and the Invoice (and Invoice Amendments) that they are related to.
Remittance ActionThis is a GraphQL object type that models the payout actions to occur, e.g. the use of Hyperwallet to pay a Seller.
Remittance EventThis is a GraphQL object type that models the events on an Remittance Action. e.g. did the action to pay a seller via Xero succeed or fail?
Payment ReleasePayment Release is a step in the remittance workflow that is undertaken by the Operator. This step is in place to ensure that Operators are only paying their Sellers when they are happy that the Seller’s Invoice has been fulfilled. This step can be performed in the Operator portal or using the remittancesRelease mutation

Object Relationships

To help contextualize some of the concepts outlined above, please refer to the relationship diagram below, (note you can fully explore the Marketplacer GraphQL schema yourself here):

Remittance Advice Relationships


Remittance Workflow

The workflow below represents a “happy path” through the remittance flow, with specific call outs to the various APIs (numbered) that can be used to automate.

Remittance Advice Relationships

From the above flow it’s worth further clarifying 2 key stages:

  • Remittance Object creation
  • Remittance Advice creation

Remittance Object Creation

Invoices

A remittance object will be created for an invoice when:

  • An Invoice has no outstanding line items
  • An invoice has at least one dispatched line item

The remittance object contains the calculated remittance and commission amounts for the entire invoice based on the configuration in Marketplacer

Invoice Amendments

The completion of the cancellation or refund process results in an invoiceAmendment object, the following conditions must be met for a remittance object to be created for an invoice amendment:

  • Invoice amendment parent invoice have been remitted

Once again referring to the object relationships (above), we can say a remittance object can be associated to single:

  • invoice
  • invoiceAmendment

Remittance Advice Creation

The daily remittance batch process runs every night to create remittance advices. One remittance advice is created per Seller and contains all the remittance objects (see section above) identified as remittable. The following conditions must be met for a remittance advice object to be created:

  • Invoice remittance delay has passed
  • Invoice payment has been released
  • Seller has remittance account details populated
    • seller.missingRemittanceDetails = false
  • If using Hyperwallet, Hyperwallet details are populated:
    • seller.missingHyperwalletDetails = false
  • If using Xero, Xero details are populated:
    • seller.missingXeroDetails = false

GraphQL Examples

Stepping through the above workflow, we’ll cover off the queries and mutations you can use at each step that are directly related to remittances and remittance advices. Note that all of these examples can be found in the Insomnia and Postman collections.

Entry Criteria

The examples that follow require that the following conditions have been met before hand:

  • An order has been created
    • For more information on creating orders with GraphQL, please refer to this article
  • The Seller has received that order (as Invoice)
  • The Seller has dispatched all the line items on the invoice

1. Release Payment

This step can be performed by the Operator using the Operator portal, or it can be achieved via API. If you want to use the API, the first thing to do would be to query for all unreleased remittances, you can do this using the remittances query to search for unreleased remittances as shown below:

query {
  remittances(
    released: false, 
    createdSince: "2023-04-10") 
  {
    nodes {
      amountCents
      id
      pendingReasons
      released
      createdAt
      invoice {
        id
        legacyId
        totalCents
        remittedAt
      }
    }
  }
}

This query relies upon the fact that remittance objects have been created, and are in an unreleased state (released = false).

In our example we have 2 unreleased remittances:

{
  "data": {
    "remittances": {
      "nodes": [
        {
          "amountCents": 3896,
          "id": "UmVtaXR0YW5jZS01MDg=",
          "pendingReasons": [
            "Payments have not been released.",
            "The remittance delay has not yet passed."
          ],
          "released": false,
          "createdAt": "2023-04-12T11:51:41+10:00",
          "invoice": {
            "id": "SW52b2ljZS0xMDQxNQ==",
            "legacyId": 10415,
            "totalCents": 4995,
            "remittedAt": null
          }
        },
        {
          "amountCents": 3896,
          "id": "UmVtaXR0YW5jZS01MDk=",
          "pendingReasons": [
            "Payments have not been released.",
            "The remittance delay has not yet passed."
          ],
          "released": false,
          "createdAt": "2023-04-12T11:58:59+10:00",
          "invoice": {
            "id": "SW52b2ljZS0xMDQxNg==",
            "legacyId": 10416,
            "totalCents": 4995,
            "remittedAt": null
          }
        }
      ]
    }
  }
}

You can see that both remittances have 2 pendingReasons why they cannot be processed:

  1. Payments have not been released (we’ll fix that shortly!)
  2. The remittance delay has not passed

To release the payment for both remittances we can use the remittancesRelease mutation as follows:

mutation {
  remittancesRelease(input: 
    { 
      remittanceIds: 
      [
        "UmVtaXR0YW5jZS01MDg="
        "UmVtaXR0YW5jZS01MDk="
      ]}){
    remittances {
      id
    }
  }
}

If you re-run the remittances query we used previously, you will not have these remittances returned (as they have now been released).

2. Querying Pending Reasons

As per the workflow you can query remittances at any time to understand what state they are in and whether there are any pending reasons as to why they have not been processed.

We have already seen an example of this query, so we don’t need to cover it again in too much detail, however it it worth mentioning that it does include a number of filters, including but not limited to:

  • createdSince timestamp
  • released - we have used this already to bring back unreleased remittances
  • processed - only return remittances when an Remittance Advice has been generated
  • retailerIds - you can supply an array of Retailer (aka Seller) Ids to limit your result set
  • pendingReasons - you can filter by the possible pending reasons a remittance may have for not being processed

For a full lits list of all the possible filters that can be used, please refer to the inline docs for the remittances query, you can find these in either:

3. Remittance Advice Webhook

Webhooks can be employed to notify interested parties when Remittance Advices are:

  • Created
  • Updated

Note: There is no delete webhook as remittances are financial objects and therefore immutable and cannot be deleted.

An example of a webhook query that can be used to shape a Remittance Advice Webhook payload can be found below:

query ($id: ID!) {
  node(id: $id) {
    ... on RemittanceAdvice {
      legacyId
      attachments {
        id
      }
      createdAt
      id
      paidAt
      paymentReference
      remittances {
        id
      }
      seller {
        id
      }
      totalCents
      totalFormatted
      totalPaidCents
      totalPaidFormatted
    }
  }
}

4. Querying Remittance Advices

A remittanceAdvice object has the following fields:

Field NameDescriptionDefined By
attachmentsSupporting documents related to actions on a remittance adviceOperator
createdAtWhen the remittance advice was createdMarketplacer
idUnique identifier of the remittance advice objectMarketplacer
legacyIdHuman readable version of the idMarketplacer
paidAtWhen the remittance advice was paidOperator
paymentReferenceAny external payment references to the paymentOperator
remittanceActionsCollection of actions taken to make payment via 3rd party system (e.g. Hyperwallet)Marketplacer
remittancesCollection of remittance objects that make up the remittance adviceMarketplacer
sellerThe seller to which the remittance advice relatesMarketplacer
totalCentsThe suggested total of the remittance advice - denominated in the lowest unit of your currency, e.g. cents or penceMarketplacer
totalCentsFormattedThe suggested total of the remittance advice, given as a formatted string for your currencyMarketplacer
totalPaidCentsThe amount paid to the seller - denominated in the lowest unit of your currency, e.g. cents or penceMarketplacer
totalPaidFormattedThe amount paid to the seller, given as a formatted string for your currencyMarketplacer

The take-away point here, (and as the name suggests) is that Remittance Advices are just an “advice” given to the Operator, it is up to the Operator to mark them as having been paid, and indeed what to pay.

You can see from the Defined By column in the above table just what fields can be updated by the Operator.

Before we do that though, let’s run a quick query to return all the Remittance Advices created since a particular date, where totalPaid is 0:

query {
  remittanceAdvices(
    createdSince: "2023-01-20T14:36:28+11:00"
    totalPaid: 0
    ) {
    nodes {
      id
      createdAt
      paidAt
      totalPaidCents
      totalCents
      remittances {
        id
        released
        amountCents
      }
      seller {
        id
        businessName
      }
      totalCents
    }
  }
}

This will return a payload similar to the following, (in this case we just get 1 Remittance Advice):

{
  "data": {
    "remittanceAdvices": {
      "nodes": [
        {
          "id": "UmVtaXR0YW5jZUFkdmljZS0yOA==",
          "createdAt": "2023-01-09T10:36:18+11:00",
          "paidAt": null,
          "totalPaidCents": 0,
          "totalCents": 2699,
          "remittances": [
            {
              "id": "UmVtaXR0YW5jZS0zMzc=",
              "released": true,
              "amountCents": 2699
            }
          ],
          "seller": {
            "id": "U2VsbGVyLTg5",
            "businessName": "ACME Inc."
          }
        }
      ]
    }
  }
}

There are a few observations with this response:

  1. There is only 1 remittance related to this advice (there can be multiple)
  2. The payment on the remittance has been released
  3. The Advice has not been paid: paidAt is null and totalPaidCents is 0

5. Mark Remittance Advice as Paid

Following on from the last step we now want to mark the Remittance as Advice as being paid, we can do this using the remittanceAdviceUpdate mutation:

mutation{
  remittanceAdviceUpdate(input:
    {
      remittanceAdviceId: "UmVtaXR0YW5jZUFkdmljZS0zMA=="
      paidAt: "2023-03-20T14:36:28+11:00"
      totalPaidCents: 2699
    })
  {
    remittanceAdvice{
      paidAt
    }
  }
}

This is quite a simplistic use of remittanceAdviceUpdate, for example you can additionally use it to attach supporting docs related to the update you provide. For a full description of the inputs you can supply, refer to the RemittanceAdviceUpdateMutationInput type found here.

Also note that if you are using the RemittanceAdvice webhook, an update event will trigger when you call remittanceAdviceUpdate.

Further financial information

This final section can be used as a reference to understand the fields available on each object that contain financial information. For example, if you require data beyond remittance per invoice to reconcile your payouts you will likely need to obtain some of the attributes below.

As an example, if you need to obtain exactly how much of the remittance is shipping you would likely include shippingCostCents for the entire invoice or postageCents for each lineItem.

ObjectFieldDescription
Remittance AdvicecommissionAmountTotalCentsThe total value of the commission within this remittance advice. This value should be equal to the sum of all remittance.commissionAmountCents
 totalCentsThe total amount of the remittance advice. This value should be equal to the sum of all remittance.amountCents
 totalPaidCentsTotal paid to the seller on this remittance advice denominated in your lowest currency unit (cents, pence etc.)

Only populated if the operator uses remittanceAdviceUpdate. This value is updated by the operator, it is not system generated
RemittanceamountCentsThe remittance amount for a single invoice or invoice amendment
 commissionAmountCentsThe commision amount for a single invoice or invoice amendment
InvoicecommissionAmountCentsSum of all lineItems.commissionAmountCents
 commissionAmountTaxCentsSum of all lineItems.commissionAmountTaxCents
 discountCentsSum of all lineItems.discountCents + any invoice discounts provided on orderCreate.

Only populated if adjustments are included in orderCreate.
 merchantFeeCentsMerchant fee set per invoice by operator.

Only populated if merchant fee is configured.
 merchantFeeTaxTotalCentsTax for merchant fee set per invoice by operator.

Only populated if merchant fee is configured.
 shippingCostCentsSum of all lineItems.postageCents
 taxShippingCentsSum of all postage tax on line items
 subTotalCentsThe invoice subtotal, excluding discounts and postage. Tax is included for tax inclusive marketplaces
 taxTotalCentsSum of (taxShippingCents + taxShippingCents + merchantFeeTaxTotalCents + commissionAmountTaxCents)
 totalCentsTotal cost of the invoice, which includes subtotals and cost of shipping if applicable
LineItemcommissionAmountCentsCommission amount calculated by platform on order creation
 commissionAmountTaxCentsTotal commission tax amount for the line item
 discountCentsThe discount applied to the item. Negative value. This only occurs when promotions are used with orderCreate.
 itemAmountCentsTotal cost per item
 postageCentsThe postage total on the item(s)
 subTotalCentsThe item subtotal, excluding discounts and postage. Tax is included for tax inclusive marketplaces
 taxTotalCentsThe tax on the item. Includes both postage and item tax
 totalCentsTotal cost of the items, which includes subtotals and cost of shipping if applicable
InvoiceAmendmentcommissionAmountTotalCentsOnly populated if the operator uses refundRequestApprove mutation
 commissionTaxTotalCentsOnly populated if the operator uses refundRequestApprove mutation
 lineItemAmountTotalCentsOnly populated if the operator uses refundRequestApprove mutation
 lineItemTaxTotalCentsOnly populated if the operator uses refundRequestApprove mutation
 remittanceAmountTotalCentsOnly populated if the operator uses refundRequestApprove mutation
 remittanceTaxTotalCentsOnly populated if the operator uses refundRequestApprove mutation
 remittanceCentsThe remittance adjustment value of the invoice amendment
 taxCentsThe tax adjustment value of the invoice amendment
 totalCentsTotal adjustment value of the invoice amendment
InvoiceAmendmentLineItemcommissionAmountCentsOnly populated if the operator uses refundRequestApprove mutation
 commissionTaxCentsOnly populated if the operator uses refundRequestApprove mutation
 lineItemAmountCentsOnly populated if the operator uses refundRequestApprove mutation
 lineItemTaxCentsOnly populated if the operator uses refundRequestApprove mutation
 remittanceAmountCentsOnly populated if the operator uses refundRequestApprove mutation
 remittanceTaxCentsOnly populated if the operator uses refundRequestApprove mutation
 amountCentsThe amount (per item) that this line item is adding to the amendment
 taxCentsTax on the line item
 totalCentsThe total that this line item is adding to the amendment