// @requirements REQ-PRODUCT-PAID-ORDER-ARCHIVES // @requirements REQ-EU-ORDER-ACTION REQ-EU-GUARANTEE-LABEL REQ-PRODUCT-CF-ADMIN REQ-PRODUCT-A11Y REQ-PRODUCT-MOBILE import {confirmationStatus} from './order-confirmation-render.js'; import {escapeHtml as e,readableTime} from './render.js'; import {renderPaidOrderReview} from './order-review.js'; import {normalizePaidOrderReview,formatPaidOrderMoney} from './order-policy.js'; import {canonicalJson,sha256} from './history.js'; import type {EvidenceRecord,JsonValue,PaidOrderArchive} from './types.js'; import type {PaidOrderReview} from './order-policy.js'; const object=(v:unknown):Record=>v&&typeof v==='object'&&!Array.isArray(v)?v as Record:{}; const text=(v:unknown)=>typeof v==='string'?v:'Not recorded'; const url=(base:string,route:string,id:string,sha256?:string)=>base+'/'+route+'?'+new URLSearchParams({id,...(sha256?{sha256}:{})}); function price(v:unknown,pricing:Record):string { const amount=object(v);if(amount.kind!=='fixed')return 'Calculated'; try{return formatPaidOrderMoney(String(amount.amountMinor),{currency:String(pricing.currency),fractionDigits:Number(pricing.fractionDigits)},'en');}catch{return 'Not recorded';} } function products(data:Record):string{return Array.isArray(data.items)?data.items.map(i=>text(object(i).name)).join(' + '):'Order request';} export function paidOrderCard(record:EvidenceRecord,basePath:string):string|undefined { const data=object(record.data);if(data.kind!=='paid-order-admission')return undefined;const pricing=object(data.pricing); return `
Request received

${e(products(data))}

${readableTime(data.occurredAt)}

Quote ${e(text(object(data.request).quoteId))}${data.confirmationDelivery?`

Confirmation · ${e(confirmationStatus(object(data.confirmationDelivery).status))}

`:''}
${e(price(pricing.totalPrice,pricing))}${e(price(pricing.dueNow,pricing))} due now
`; } const facts=(rows:[string,unknown][])=>`
${rows.map(([label,value])=>`
${e(label)}
${e(text(value))}
`).join('')}
`; function heading(data:Record):string { return `

Order request

${e(products(data))}

Received ${readableTime(data.occurredAt)}

${[['Request','Received'],['Payment','Not reported'],['Fulfillment','Not reported'],['Contract',data.confirmationDelivery?'Reported by host':'Not reported'],['Confirmation',confirmationStatus(object(data.confirmationDelivery).status)]].map(([label,value])=>`
${label}${value}
`).join('')}
`; } export function paidOrderDetail(record:EvidenceRecord,basePath:string,archiveEnabled:boolean,confirmationEnabled=false):string|undefined { const data=object(record.data);if(data.kind!=='paid-order-admission')return undefined;const request=object(data.request),pricing=object(data.pricing); return `
← Back to evidence search${heading(data)}

Request context

${facts([['Request ID',request.requestId],['Quote',request.quoteId],['Quote revision',request.quoteRevision],['Site',data.siteId],['Workflow scope',data.scopeId]])}

Information presented

The retained definition and quote identify the information supplied for this request. Payment, fulfillment and contract formation are separate host operations.

${archiveEnabled?`Inspect retained review `:'

Complete review inspection requires a separate permission from your host.

'}

Contract confirmation

${e(confirmationStatus(object(data.confirmationDelivery).status))}. The admission receipt above remains the original capture; delivery status is current operational information.

${confirmationEnabled&&data.confirmationDelivery?`Inspect customer confirmation ↗`:data.confirmationDelivery?'

Complete confirmation inspection requires separate permission.

':'

Your host can prepare the complete confirmation after reporting contract conclusion.

'}
Evidence references${facts([['Admission ID',data.id],['Review ID',data.reviewId],['Definition ID',request.definitionId],['Checkout reference',data.streamRef],['Request SHA-256',data.requestSha256]])}
Inspect record JSON
${e(JSON.stringify(record.data,null,2))}
`; } /** A custom host store must return the exact retained review bound to this admission. */ export async function validateOrderArchive(input:unknown,record:EvidenceRecord):Promise{ if(!input||typeof input!=='object'||![Object.prototype,null].includes(Object.getPrototypeOf(input)))throw Error('Invalid order archive.'); const fields=Object.getOwnPropertyDescriptors(input),data=object(record.data);if(Reflect.ownKeys(fields).length!==2||!fields.id||!fields.review||!('value' in fields.id)||!('value' in fields.review)||!fields.id.enumerable||!fields.review.enumerable||fields.id.value!==record.id||data.kind!=='paid-order-admission')throw Error('Invalid order archive.'); const review=normalizePaidOrderReview(fields.review.value,{allowPreview:true}),{reviewId,...content}=review,{definitionId,...definition}=review.definition,request=object(data.request); if(reviewId!==data.reviewId||request.reviewId!==reviewId||request.definitionId!==definitionId||request.quoteId!==review.quote.quoteId||request.quoteRevision!==review.quote.revision||review.quote.contextRef!==data.streamRef||review.definition.siteId!==data.siteId||await sha256(canonicalJson(content as unknown as JsonValue))!==reviewId||await sha256(canonicalJson(definition as unknown as JsonValue))!==definitionId)throw Error('Invalid order archive.'); return {id:record.id,review}; } export function orderArchiveBody(archive:PaidOrderArchive,record:EvidenceRecord,basePath:string):string { const review=archive.review,data=object(record.data),files=new Map();let rendered=renderPaidOrderReview(review,{idPrefix:'retained-order',headingLevel:3,allowPreview:true}); for(const item of review.definition.items)if(item.guarantee?.status==='label-provided'){ const asset=item.guarantee.label.asset;files.set(asset.sha256,item); rendered=rendered.replaceAll('src="'+asset.url+'"','src="'+e(url(basePath,'order-image',record.id,asset.sha256))+'"').replaceAll('href="'+asset.url+'"','href="'+e(url(basePath,'order-asset',record.id,asset.sha256))+'"'); } return `
← Back to order request${heading(data)}

Payment action shown

${e(review.definition.wording.paymentActionLabel)}

Retained wording only. This archive cannot place an order.

Retained review

The original facts, price schedule and wording bound to the request. This rendering does not prove a person saw or understood them.

${rendered}

Archive files

Download review JSON ↓${[...files].map(([sha,item])=>{const asset=item.guarantee!.status==='label-provided'?item.guarantee!.label.asset:null;return asset?`
Original label · ${e(item.name)} ↓${asset.bytes.toLocaleString('en')} bytes · ${e(asset.mediaType)}${sha}
`:'';}).join('')}
Quote and review references${facts([['Quote ID',review.quote.quoteId],['Quote revision',review.quote.revision],['Issued at',review.quote.issuedAt],['Expires at',review.quote.expiresAt],['Review SHA-256',review.reviewId],['Definition SHA-256',review.definition.definitionId]])}
`; }