This repository has no description
JavaScript 100%

README.md

florist #

A florist on the open network: a Fabric/Beckn v2 provider node that publishes a flower catalogue to the NFH Catalog Service (CATALG) and responds to sales (select → init → confirm) from any consumer node on the network.

Zero runtime dependencies — plain Node.js ≥ 20 (node:crypto provides the Ed25519 signing and BLAKE2b-512 digests the protocol needs).

What's in the shop #

Five flowers, defined in data/catalog.json as Fabric resources with priced offers (plus a provider-level free-delivery offer):

Item Price
ITEM-ROSE-RED-DOZEN — Red Roses, dozen €45.00
ITEM-TULIP-MIXED-10 — Mixed Tulips, bunch of 10 €12.50
ITEM-PEONY-PINK-5 — Pink Peonies, bunch of 5 (seasonal offer) €24.00
ITEM-SUNFLOWER-5 — Sunflowers, bunch of 5 €15.00
ITEM-LILY-STARGAZER-3 — Stargazer Lilies, bunch of 3 €18.50

Local stock levels live in data/inventory.json and never leave this node; they gate quotes and get decremented on confirm (and restored on cancel).

Quick start #

# 1. Generate your Ed25519 signing key pair
node bin/florist.js keygen

# 2. Look at the catalogue, check it against CATALG's rules
node bin/florist.js catalog show
node bin/florist.js catalog validate

# 3. Run the provider node (responds to sales, receives publish results)
node bin/florist.js serve

# 4. Publish the catalogue to the network
node bin/florist.js publish

npm test runs the suite, including a full end-to-end signed sales flow against a live server instance.

Joining the network #

Before the live network will accept your publishes, your identity must exist in the DeDi registry (see Onboarding network participants):

  1. florist keygen prints the Base64 raw public key the registry wants.
  2. Create an account on dedi.global, verify your namespace (domain TXT record), create a registry from the Fabric subscriber schema, and publish a subscriber record with your callback base URL (e.g. https://florist.berjon.com/beckn/) and that public key. Note the record id.
  3. Set subscriberId (namespace_id/registry_id) and keyRecordId in config/florist.config.json to match, and bppUri to your public HTTPS base URL. CATALG resolves your /catalog/on_publish callback from the registry entry, not from the request body, so the registered URL must reach the machine running florist serve.
  4. Check the registration: https://fabric.nfh.global/registry/dedi/lookup/<subscriberId>/<recordId>

Every outbound request is signed with a Beckn HTTP Signature: Ed25519 over the (created) (expires) digest signing string, where digest is BLAKE-512={base64-blake2b-512-of-body}.

Tooling #

Publish the catalogue #

florist publish             # MERGE — upsert only what changed
florist publish --full      # FULL — replace the entire published set

POSTs a signed catalog/publish envelope to the configured Catalog Service. The synchronous ACK means "accepted for processing"; the real per-catalog verdict (ACCEPTED / REJECTED / PARTIAL) arrives asynchronously at /beckn/catalog/on_publish — keep florist serve running to receive it. Results are logged and stored in data/last-publish-result.json.

Add / manage entries #

florist catalog add --name "Ranunculus — Bunch of 8" --price 16.5 \
    --botanical "Ranunculus asiaticus" --color coral --stems 8 --stock 20
florist catalog price --id ITEM-RANUNCULUS-BUNCH-OF-8 --price 18
florist catalog remove --id ITEM-RANUNCULUS-BUNCH-OF-8   # then publish --full
florist stock --id ITEM-ROSE-RED-DOZEN --set 36
florist catalog validate

add creates the resource (with RetailResource JSON-LD attributes) and a matching offer with a PriceSpecification consideration in one step. After any change, florist publish pushes it to the network — MERGE mode means you only ship the delta. Removals need publish --full, since MERGE preserves already-published resources.

Respond to sales #

florist serve                # default port 4402
florist orders               # list received orders
florist orders show --txn <transactionId>

The server implements the Fabric contracting flow on the /beckn/{endpoint} pattern:

Endpoint What the florist does
POST /beckn/select Prices the basket against catalogue + stock, ACKs, sends a signed on_select quote
POST /beckn/init Attaches billing/fulfillment, sends on_init with the draft contract and payment terms
POST /beckn/confirm Commits the order, decrements stock, assigns an order id, sends on_confirm
POST /beckn/status Sends on_status with the current contract state
POST /beckn/cancel Cancels, restores stock, sends on_cancel
POST /beckn/catalog/on_publish Records CATALG's publish verdict
POST /beckn/on_discover Receives discovery results for florist discover smoke tests

Every callback is signed with your key and carries a requestDigest binding it to the originating request. Orders persist as JSON under data/orders/, one file per transactionId, moving through QUOTED → INITIALIZED → CONFIRMED (or CANCELLED).

Check discoverability #

florist discover --text "red roses"

Sends a signed discover intent to the configured Discovery Service; results arrive at /beckn/on_discover and land in data/last-discover-result.json.

Configuration #

config/florist.config.json (each key overridable via FLORIST_* env vars, see src/config.js):

Key Meaning
bppId / bppUri Your provider-node identity and public Beckn base URL
subscriberId / keyRecordId DeDi registry identity — these form the signature keyId
catalogServiceUrl CATALG base (default https://fabric.nfh.global/beckn/catalog)
discoveryServiceUrl A DISCOVR instance, for florist discover
registryUrl DeDi registry (default https://fabric.nfh.global/registry/dedi)
visibleTo Network ids to scope distribution to; empty = default global network nfh.global/beckn-nodes
verifyInboundSignatures Verify consumer signatures against the registry — turn on for anything network-facing
trustedKeys "subscriberId|recordId" → public key map for offline/dev verification

Layout #

bin/florist.js       CLI
src/signing.js       Beckn HTTP Signature (Ed25519 + BLAKE2b-512 digest)
src/envelope.js      Fabric v2 context / ACK / NACK envelopes
src/registry.js      DeDi registry lookups (key + callback resolution)
src/client.js        Signed outbound POSTs
src/catalog.js       Catalogue CRUD + publish payload construction
src/publish.js       catalog/publish flow
src/orders.js        Contract state machine (quote → init → confirm → cancel)
src/server.js        The provider node itself
data/catalog.json    The published Fabric Catalog object
data/inventory.json  Local-only stock
test/                Signing, catalogue, and end-to-end sales-flow tests

Notes #

  • The flower attributes (botanicalName, stemsPerUnit, …) extend the RetailResource 2.1 schema in JSON-LD open-world fashion. If CATALG's domain-schema validation rejects them (on_publish → REJECTED with SCH_SCHEMA_VALIDATION_FAILED), trim resourceAttributes down to the fields the retail schema defines and republish.
  • Provider location/address in data/catalog.json is a placeholder — set your real coordinates ([longitude, latitude]) before publishing, since spatial discovery indexes provider.availableAt[*].geo.
  • Fulfillment-stage actions beyond status/cancel (update, track) and post-fulfillment (rate, support) are not implemented yet; the server NACKs them with CTX_ACTION_MISMATCH.