Skip to content

After validating a customer return, calculate the accepted credit from your authoritative transaction data and submit a definitive credit-note generation input. The SDK resolves the original receipt's owner devices, encrypts the same input independently for each device, and submits it through the credit-note endpoint.

Cheqi does not calculate or move the refund

Your system owns eligibility, accepted quantity, financial and tax calculations, payment processing, reconciliation, and customer communication. The SDK serializes the values you provide without recalculating them.

Amount signs

Supply positive quantities and positive credit amounts. A credit note represents the reversal through its document type; negative quantities or totals are not required and produce incorrect UBL semantics for the current generator.

For example, a full credit of a €100.00 net line with 21% VAT uses:

  • quantity: 1
  • subtotal: 100.00
  • totalTaxAmount: 21.00
  • totalAmount: 121.00

Generation input

The plaintext encrypted for each owner device has two top-level properties:

PropertyTypeRequiredDescription
creditNoteTemplateRequestobjectYesDefinitive merchant-supplied credit-note values.
taxesAppliedbooleanYesSet to false when no tax applies; otherwise provide tax entries with values.
{
  "creditNoteTemplateRequest": {
    "documentNumber": "CN-2026-0042",
    "originatorDocumentReference": "INV-2026-0042",
    "identifiers": [
      { "type": "RETURN_AUTHORIZATION", "value": "RMA-91827" }
    ],
    "issueDate": "2026-08-05T14:30:22Z",
    "currency": "EUR",
    "creditNoteSubtotal": 100.00,
    "totalBeforeTax": 100.00,
    "totalTaxAmount": 21.00,
    "totalAmount": 121.00,
    "products": [
      {
        "identifier": "SKU-AIRMAX-BLK-42",
        "brandName": "Nike",
        "name": "AirMax",
        "quantity": 1,
        "baseQuantity": 1,
        "unitCode": "C62",
        "unitPrice": 100.00,
        "subtotal": 100.00,
        "total": 121.00,
        "taxes": [
          {
            "rate": 21,
            "type": "VAT",
            "taxableAmount": 100.00,
            "amount": 21.00,
            "category": "STANDARD"
          }
        ]
      }
    ],
    "taxes": [
      {
        "rate": 21,
        "type": "VAT",
        "taxableAmount": 100.00,
        "amount": 21.00,
        "label": "VAT 21%",
        "category": "STANDARD"
      }
    ]
  },
  "taxesApplied": true
}

C62 is the UN/ECE unit code for one item. Use the unit and tax category appropriate to the original transaction.

Required credit-note properties

PropertyDescription
documentNumberYour unique credit-note number.
originatorDocumentReferenceYour document number for the original receipt.
issueDateISO 8601 issue date and time.
currencyISO 4217 code.
creditNoteSubtotalLine subtotal before document-level adjustments and tax.
totalBeforeTaxTotal after document-level adjustments, excluding tax.
totalTaxAmountTotal tax credit.
totalAmountTax-inclusive total credit.
productsAt least one credited product.

Every product needs name, identifier, quantity, unitCode, unitPrice, subtotal, and total. The identifier should match the return request's productId and the original receipt product's identifier.

When taxesApplied is true, include document-level tax entries and the relevant product-level entries. Each tax needs rate, type, and taxableAmount; include amount for the calculated tax credit. When tax does not apply, set taxesApplied to false and the tax arrays may be empty.

Optional document properties are identifiers, discounts, charges, period, and jurisdictionalData. The legacy note property is ignored by the current document generator; configure the company's receipt text when a seller note should appear.

See Credit Notes for the full property summary.

Build from a return request

Use request.productId and the parent receipt identifiers to look up the original line in your own system. Do not trust prices or derive refund eligibility from customer input.

function buildCreditNoteGenerationInput(returnRequest, originalReceipt) {
  const acceptedLines = validateAndPriceReturn(returnRequest, originalReceipt);

  const products = acceptedLines.map(({ originalLine, acceptedQuantity }) => {
    const subtotal = calculateNetCredit(originalLine, acceptedQuantity);
    const tax = calculateTaxCredit(originalLine, acceptedQuantity);

    return {
      identifier: originalLine.identifier,
      brandName: originalLine.brandName,
      name: originalLine.name,
      quantity: acceptedQuantity,
      baseQuantity: originalLine.baseQuantity ?? 1,
      unitCode: originalLine.unitCode,
      unitPrice: originalLine.unitPrice,
      subtotal,
      total: subtotal + tax,
      taxes: [{
        rate: originalLine.taxRate,
        type: originalLine.taxType,
        taxableAmount: subtotal,
        amount: tax,
        category: originalLine.taxCategory
      }]
    };
  });

  const totalBeforeTax = products.reduce((sum, line) => sum + line.subtotal, 0);
  const totalTaxAmount = products.reduce(
    (sum, line) => sum + line.taxes.reduce((taxSum, tax) => taxSum + tax.amount, 0),
    0
  );

  return {
    creditNoteTemplateRequest: {
      documentNumber: generateCreditNoteNumber(),
      originatorDocumentReference: returnRequest.receiptId,
      issueDate: new Date().toISOString(),
      currency: originalReceipt.currency,
      creditNoteSubtotal: totalBeforeTax,
      totalBeforeTax,
      totalTaxAmount,
      totalAmount: totalBeforeTax + totalTaxAmount,
      products,
      taxes: aggregateTaxes(products)
    },
    taxesApplied: originalReceipt.taxesApplied
  };
}

Use decimal-safe arithmetic in production; the example leaves those business-specific functions to your implementation.

Issue with JavaScript or TypeScript

Identify the recipient through the parent cheqiReceiptId, then pass that same ID separately as parentCheqiReceiptId:

const parentCheqiReceiptId = returnRequest.cheqiReceiptId;
const identification = { cheqiReceiptId: parentCheqiReceiptId };
const generationInput = buildCreditNoteGenerationInput(
  returnRequest,
  originalReceipt
);

const result = await sdk.creditNoteService.issueCreditNote(
  identification,
  parentCheqiReceiptId,
  generationInput
);

if (result.isAccepted()) {
  console.log("Credit note accepted", result.cheqiReceiptId);
}

For OAuth, pass the access token as the fourth argument. To specify a store and OAuth token, pass storeId as the fourth argument and the token as the fifth.

Issue with Java

The current Java method accepts the generation input as an object while the public typed schema is being finalized:

Map<String, Object> generationInput = Map.of(
    "creditNoteTemplateRequest", creditNoteTemplateRequest,
    "taxesApplied", taxesApplied
);

IdentificationDetails identification = new IdentificationDetails()
    .cheqiReceiptId(returnRequest.getCheqiReceiptId());

CreditNoteResult result = sdk.getCreditNoteService().issueCreditNote(
    identification,
    returnRequest.getCheqiReceiptId(),
    generationInput
);

if (result.isAccepted()) {
    log.info("Credit note accepted: {}", result.getCheqiReceiptId());
}

The OAuth overload takes accessToken after the generation input. An overload with storeId and accessToken is also available.

Result and lifecycle

An accepted SDK result contains:

PropertyDescription
cheqiReceiptIdIdentifier assigned to the new credit-note generation job/document.
parentCheqiReceiptIdOriginal receipt ID supplied by the merchant.
matchIdRecipient-resolution match identifier.
statusInitial submission status.
createdAtSubmission timestamp.

Acceptance means the encrypted generation job was queued; it does not mean a payment refund has been completed. Update the original return request's status separately using its requestId, and only mark it COMPLETED when your return workflow has actually finished.