Skip to main content

Fulfillment templating

Fulfillment templates control the HTTP call Nexway makes to a partner's server during fulfillment. A template defines the URL path, request body, headers, and how Nexway should extract values from the partner's response. Templates are configured per integration by the Nexway operations team.

For the high-level fulfillment flow and request payload field reference, see How fulfillment works.

How a template is used

Each integration carries one template definition per fulfillment operation (create, cancel, renew, upgrade, pause, resume). At call time Nexway:

  1. Picks the template matching the operation. If no per-operation template is defined, a fallback template is used.
  2. Renders the URL path and request body against the order's data context.
  3. Sends a POST request with the rendered body and the operation's configured headers.
  4. Parses the response with the operation's JSONPath extractors and stores the extracted values on the fulfillment.

Template configuration

A template definition has four parts, each scoped to one operation:

PartPurpose
urlComplementThe URL path appended to the partner's base URL. Supports template syntax.
bodyTemplateThe request body, rendered to JSON. Supports template syntax.
httpHeadersMap of request headers added on top of the integration's default headers.
responsePathsMap of JSONPath expressions used to extract values from the partner's response.

Template syntax

Templates use Go-style text/template syntax. The runtime supports the constructs partners typically need:

  • Field access. {{.Checkout.OrderID}} — references a value in the data context.
  • Conditional block. {{if eq .Operation "create"}}new{{end}} — emits content only if the predicate is true.
  • Optional field. {{- with .User.FirstName -}}, "firstName": "{{.}}"{{- end -}} — emits the block only when the field is non-empty. The dash trims surrounding whitespace.
  • Pipes. {{ convertToJson .Product.Variables }} — passes a value through a function.

Template context

The data context is the root object passed to the template. All fields are referenced with PascalCase identifiers (matching the underlying struct).

Top level

FieldTypeDescription
LicenseIDstringFulfillment identifier (UUID)
OperationstringOne of create, cancel, renew, upgrade, pause, resume
OperationExecutionIDstringIdentifier of this specific execution attempt
RequestTimestampnumberEpoch milliseconds when the request was queued
CheckoutobjectOrder-level data (see below)
UserobjectBuyer data (see below)
ProductobjectProduct data (see below)
AdditionalDatamapArbitrary key-value pairs; values are lists of strings

Checkout

FieldType
OrderIDstring
SubscriptionIDstring
CartExternalContextstring
StoreExternalContextstring
AffiliateIDstring
ResellerIDstring
BillingPlanIDstring
ProductUsageIDstring
TrialContextstring
Price.GrossPricenumber
Price.Currencystring

User

FieldType
IDstring
Emailstring
FirstNamestring
LastNamestring
CompanyNamestring
Streetstring
Citystring
ZipCodestring
Countrystring
Localestring

Product

FieldType
IDstring
PublisherProductIDstring
PublisherFulfillmentIDstring
LineItemIDstring
Namestring
ExternalContextstring
StartTimestampnumber (epoch ms)
ExpirationTimestampnumber (epoch ms)
Quantitynumber
Price.GrossPricenumber
Price.Currencystring
PriceFunctionParametersmap(string,string)
Variablesmap(string,string)
ActivationLinkstring

AdditionalData

A flat map of arbitrary keys to lists of strings. Common entries:

KeyOrigin
ActivationCodeActivation code from a previous fulfillment (renewals)
PublisherLicenseIDLicense identifier returned by the partner

Other keys may be present depending on the integration.

Custom functions

FunctionSignaturePurpose
convertToJsonconvertToJson valueSerializes a value to a JSON literal.
timestampToRFC3339timestampToRFC3339 epochMillisConverts epoch milliseconds to ISO 8601 UTC.
defaultdefault value fallbackReturns fallback if value is null or empty.
eqeq a bString equality. Used in if.
ne, lt, le, gt, gecomparison operatorsNumeric or string comparison.
not, and, orlogic operatorsCombine boolean predicates.
lenlen valueLength of a string, list, or map.
indexindex collection keyIndexes into a list or map.
sliceslice value start endSubstring or sublist.
print, printf, printlnstring formattingFormat helpers.

Response extraction

Each operation defines a responsePaths map. Keys are extraction names; values are JSONPath expressions evaluated against the response body. Extracted values are stored on the fulfillment and (for known names) drive success or failure decisions.

Standard extraction names

NamePurpose
activationCodeDelivered license key, activation code, or serial number
activationFileContentFile content (e.g. a certificate) to deliver alongside the key
activationLinkURL for activating the license
successFlagMust equal "true" to count as success when present
errorCodeNon-empty value marks the call as failed
errorMessageHuman-readable error detail

Any other extraction names are also captured and stored alongside the fulfillment as additional data.

JSONPath suffix

A path ending in + (for example $.licenses[0].activationCode+) is treated as a list-extraction — Nexway captures all matching values rather than the first one.

Response value conversion

Each extraction entry can carry an optional conversionTemplate — a small template applied to the extracted value before it is stored. Use this to reshape or normalize values (e.g. strip a prefix, build a URL around a code).

Default fulfillment template

This is the template body Nexway sends by default when no custom fulfillment template is configured for a partner. The operations team uses it as a starting point when setting up new integrations or reviewing existing ones.

A rendered payload example and field definitions are in How fulfillment works.

{
"fulfillmentId": "{{.LicenseID}}",
"checkout": {
"orderId": "{{.Checkout.OrderID}}",
"lineItemId": "{{.Checkout.LineItemID}}",
{{- with .Checkout.SubscriptionID }}
"subscriptionId": "{{.}}",
{{- end }}
{{- with .Checkout.CartExternalContext }}
"cartExternalContext": "{{.}}",
{{- end }}
{{- with .Checkout.TrialContext }}
"trialContext": "{{.}}",
{{- end }}
"price": {
"grossPrice": {{.Checkout.Price.GrossPrice}},
"currency": "{{.Checkout.Price.Currency}}"
}
},
"user": {
"id": "{{.User.ID}}",
"email": "{{.User.Email}}",
"country": "{{.User.Country}}",
"locale": "{{.User.Locale}}"
{{- with .User.FirstName }},
"firstName": "{{.}}"
{{- end }}
{{- with .User.LastName }},
"lastName": "{{.}}"
{{- end }}
{{- with .User.CompanyName }},
"companyName": "{{.}}"
{{- end }}
{{- with .User.CompanyIdentifier }},
"companyIdentifier": "{{.}}"
{{- end }}
{{- with .User.City }},
"city": "{{.}}"
{{- end }}
{{- with .User.ZipCode }},
"zipCode": "{{.}}"
{{- end }}
},
"product": {
"id": "{{.Product.ID}}",
"name": "{{.Product.Name}}"
{{- with .Product.PublisherProductID }},
"publisherProductId": "{{.}}"
{{- end }}
{{- with .Product.ExternalContext }},
"externalContext": "{{.}}"
{{- end }}
{{- with .Product.PriceFunctionParameters }},
"priceFunctionParameters": {{ convertToJson . }}
{{- end }}
{{- with .Product.Variables }},
"variables": {{ convertToJson . }}
{{- end }},
"price": {
"grossPrice": {{.Product.Price.GrossPrice}},
"currency": "{{.Product.Price.Currency}}"
}
}
}

Example: a single-endpoint integration

A partner exposes one endpoint that handles all operations, distinguishing them by URL suffix. The integration is configured with:

urlComplement

/licenses/{{if eq .Operation "create"}}new{{end}}{{if eq .Operation "renew"}}renew{{end}}{{if eq .Operation "cancel"}}cancel{{end}}{{if eq .Operation "upgrade"}}upgrade{{end}}

bodyTemplate

{
"fulfillmentId": "{{.LicenseID}}",
"orderId": "{{.Checkout.OrderID}}",
"productCode": "{{.Product.PublisherProductID}}",
"buyerEmail": "{{.User.Email}}"
{{- with .AdditionalData.ActivationCode }},
"existingActivationCode": "{{.}}"
{{- end }}
}

responsePaths

NameJSONPath
activationCode$.result.licenseKey
errorCode$.error.code
errorMessage$.error.message