@@ -208,7 +208,7 @@ function EdgeCard({ edge }) {
208208}
209209
210210// the explainer - what the app actually does, end to end, threaded with the selected fixture's numbers
211- function Pipeline ( { edge, source, settleRes } ) {
211+ function Pipeline ( { edge, source, settleRes, procurementRes } ) {
212212 const fav = edge ?. fair ?. favourite
213213 const steps = [
214214 { n : 1 , title : 'Verified data' ,
@@ -220,6 +220,9 @@ function Pipeline({ edge, source, settleRes }) {
220220 { n : 3 , title : 'Settled by a neutral arbiter' ,
221221 desc : 'The buyer funds a per-order escrow but cannot unilaterally refund - a trusted neutral arbiter program releases to the seller on verified delivery. The escrow reference is bound to the read (sha256), so the on-chain order IS the data bought. Real devnet txs, linked on Explorer.' ,
222222 live : settleRes ?. ok ? `${ settleRes . amountSol } SOL${ settleRes . mode === 'arbiter' ? ' - arbiter' : '' } ` : `${ SETTLE_SOL } SOL` } ,
223+ { n : 4 , title : 'Optional upstream spend' ,
224+ desc : 'The seller can buy an upstream API through the Pay.sh rail before delivery. The run ledger stores PAYMENT_REQUIRED, PAYMENT_PROOF, and PAYMENT_CONFIRMED alongside the escrow settlement.' ,
225+ live : procurementRes ?. procurement ?. paid ? `${ procurementRes . procurement . amount } ${ procurementRes . procurement . currency } ` : 'Pay.sh' } ,
223226 ]
224227 return html `
225228 < section class ="pipeline ">
@@ -230,7 +233,7 @@ function Pipeline({ edge, source, settleRes }) {
230233 < div class ="pipe-h "> < span class ="pipe-n "> 0${ s . n } </ span > < span class ="pipe-live "> ${ s . live } </ span > </ div >
231234 < h4 > ${ s . title } </ h4 >
232235 < p > ${ s . desc } </ p >
233- ${ i < 2 && html `< span class ="pipe-arrow "> -> </ span > ` }
236+ ${ i < steps . length - 1 && html `< span class ="pipe-arrow "> -> </ span > ` }
234237 </ div > ` ) }
235238 </ div >
236239 </ section > `
@@ -288,6 +291,21 @@ function SettleResult({ r }) {
288291 < a href =${ addrLink ( ESCROW_PROGRAM ) } target ="_blank" rel="noreferrer"> escrow program open</ a > </ div > `
289292}
290293
294+ function ProcurementResult ( { r } ) {
295+ if ( ! r ) return null
296+ if ( r . ok ) return html `
297+ < div class ="settled ok pay-sh-result ">
298+ < div class ="settled-line "> < span class ="bind-tag "> Pay.sh</ span >
299+ seller procured upstream context for < b > ${ r . procurement ?. amount } ${ r . procurement ?. currency } </ b >
300+ then settled < b > ${ r . amountSol } SOL</ b > to the seller
301+ </ div >
302+ < div class ="settled-line bind ">
303+ proof < b > ${ shortAddr ( r . procurement ?. proof ) } </ b > stored in the run ledger as PAYMENT_PROOF
304+ </ div >
305+ </ div > `
306+ return html `< div class ="settled sim "> Pay.sh demo unavailable${ r . error ? ` (${ String ( r . error ) . slice ( 0 , 70 ) } )` : '' } </ div > `
307+ }
308+
291309function RunsPanel ( { runs, selectedRun, onSelect, onGrade, grading } ) {
292310 const top = selectedRun ?? runs ?. [ 0 ]
293311 const outcome = top ?. outcome
@@ -324,6 +342,7 @@ function RunsPanel({ runs, selectedRun, onSelect, onGrade, grading }) {
324342 < span > request < b > ${ top . want ?. arg } </ b > </ span >
325343 ${ top . delivery ?. sha256 && html `< span > delivery sha < b > ${ shortAddr ( top . delivery . sha256 ) } </ b > </ span > ` }
326344 ${ top . escrow ?. reference && html `< span > escrow ref < b > ${ shortAddr ( top . escrow . reference ) } </ b > </ span > ` }
345+ ${ top . verification ?. upstreamPayment ?. rail && html `< span > upstream < b > ${ top . verification . upstreamPayment . rail } ${ top . verification . upstreamPayment . amount } ${ top . verification . upstreamPayment . currency } </ b > </ span > ` }
327346 ${ outcome && html `< span > reality < b > ${ outcome . status === 'graded'
328347 ? `${ outcome . actual ?. winner ?? 'unknown' } - ${ outcome . correct === true ? 'hit' : outcome . correct === false ? 'miss' : 'unscored' } `
329348 : 'pending' } </ b > </ span > `}
@@ -398,6 +417,8 @@ function App() {
398417 const [ loadingOdds , setLoadingOdds ] = useState ( false )
399418 const [ edge , setEdge ] = useState ( null )
400419 const [ settleRes , setSettleRes ] = useState ( null )
420+ const [ procurementRes , setProcurementRes ] = useState ( null )
421+ const [ procuring , setProcuring ] = useState ( false )
401422 const [ settling , setSettling ] = useState ( false )
402423 const [ runs , setRuns ] = useState ( null )
403424 const [ selectedRun , setSelectedRun ] = useState ( null )
@@ -423,6 +444,21 @@ function App() {
423444 finally { setGrading ( false ) }
424445 }
425446
447+ const runPayShDemo = async ( ) => {
448+ if ( ! selected || source !== 'live' ) return
449+ setProcuring ( true )
450+ setProcurementRes ( null )
451+ try {
452+ const r = await ( await fetch ( `${ PROXY } /api/pay-sh-edge?fixtureId=${ selected . FixtureId } &amount=${ SETTLE_SOL } &upstreamUsdc=0.03` ) ) . json ( )
453+ setProcurementRes ( r )
454+ await loadRuns ( )
455+ } catch ( e ) {
456+ setProcurementRes ( { ok : false , error : String ( e ?. message ?? e ) } )
457+ } finally {
458+ setProcuring ( false )
459+ }
460+ }
461+
426462 // load the board: fixtures with verified live odds (inlined). The free World Cup tier's odds are
427463 // intermittent and the proxy needs a few seconds to subscribe on a cold start, so we KEEP polling
428464 // until live data arrives - showing the labelled sample board meanwhile, then switching to live on
@@ -486,7 +522,7 @@ function App() {
486522 useEffect ( ( ) => {
487523 if ( ! selected ) return
488524 let alive = true
489- setEdge ( null ) ; setSettleRes ( null ) ; setSettling ( false )
525+ setEdge ( null ) ; setSettleRes ( null ) ; setProcurementRes ( null ) ; setSettling ( false )
490526 ; ( async ( ) => {
491527 // 1) the agent's call
492528 let e = clientEdge ( selected )
@@ -525,7 +561,7 @@ function App() {
525561 turns it into < b > fair (break-even) odds + a plain read</ b > , and gets paid through an on-chain escrow.</ p >
526562 </ header >
527563 < main >
528- < ${ Pipeline } edge =${ edge } source=${ source } settleRes=${ settleRes } />
564+ < ${ Pipeline } edge =${ edge } source=${ source } settleRes=${ settleRes } procurementRes= ${ procurementRes } />
529565 ${ ! fixtures && html `< p class ="muted " style =${ { textAlign : 'center' } } > loading fixtures...</ p > ` }
530566 ${ selected && html `
531567 < section class ="featured ">
@@ -546,6 +582,10 @@ function App() {
546582 < span class ="spin "> </ span > agent delivered - arbiter settling ${ SETTLE_SOL } SOL in escrow on devnet...
547583 </ div > ` }
548584 ${ settleRes && html `< ${ SettleResult } r =${ settleRes } /> ` }
585+ < button class ="pay-sh-btn " disabled =${ source !== 'live' || procuring } onClick =${ runPayShDemo } >
586+ ${ procuring ? html `< span class ="spin "> </ span > procuring + settling...` : 'Run Pay.sh procurement demo' }
587+ </ button >
588+ ${ procurementRes && html `< ${ ProcurementResult } r =${ procurementRes } /> ` }
549589 ${ selected && html `< ${ PayButton } fixture =${ selected } /> ` }
550590 </ div >
551591 </ div >
0 commit comments