@@ -414,9 +414,6 @@ private async Task<ResponseContent> ProcessCheckpoint(string clientId, Uri selle
414414 }
415415 public async Task < ResponseContent > ProcessOrderCreationB ( string clientId , Uri sellerId , string uuidString , string orderJson , Uri customerAccountId = null )
416416 {
417-
418- // Note B will never contain OrderItem level errors, and any issues that occur will be thrown as exceptions.
419- // If C1 and C2 are used correctly, B should not fail except in very exceptional cases.
420417 Order order = OpenActiveSerializer . Deserialize < Order > ( orderJson ) ;
421418 if ( order == null || order . GetType ( ) != typeof ( Order ) )
422419 {
@@ -425,21 +422,23 @@ public async Task<ResponseContent> ProcessOrderCreationB(string clientId, Uri se
425422 var ( orderId , sellerIdComponents , seller , customerAccountIdComponents ) = await ConstructIdsFromRequest ( clientId , sellerId , customerAccountId , uuidString , OrderType . Order ) ;
426423 using ( await asyncDuplicateLock . LockAsync ( GetParallelLockKey ( orderId ) ) )
427424 {
425+ // Attempt to use idempotency cache if it exists
426+ var cachedResponse = await GetResponseFromIdempotencyStoreIfExists ( settings , orderId , orderJson ) ;
427+ if ( cachedResponse != null )
428+ {
429+ return cachedResponse ;
430+ }
431+
428432 var response = order . OrderProposalVersion != null ?
429433 await ProcessOrderCreationFromOrderProposal ( orderId , settings . OrderIdTemplate , seller , sellerIdComponents , customerAccountIdComponents , order ) :
430434 await ProcessFlowRequest ( ValidateFlowRequest < Order > ( orderId , sellerIdComponents , seller , customerAccountIdComponents , FlowStage . B , order ) , order ) ;
431435
432- // Return a 409 status code if any OrderItem level errors exist
433- return ResponseContent . OpenBookingResponse ( OpenActiveSerializer . Serialize ( response ) ,
434- response . OrderedItem . Exists ( x => x . Error ? . Count > 0 ) ? HttpStatusCode . Conflict : HttpStatusCode . Created ) ;
436+ return await CreateResponseViaIdempotencyStoreIfExists ( settings , orderId , orderJson , response ) ;
435437 }
436438 }
437439
438440 public async Task < ResponseContent > ProcessOrderProposalCreationP ( string clientId , Uri sellerId , string uuidString , string orderJson , Uri customerAccountId = null )
439441 {
440-
441- // Note B will never contain OrderItem level errors, and any issues that occur will be thrown as exceptions.
442- // If C1 and C2 are used correctly, P should not fail except in very exceptional cases.
443442 OrderProposal order = OpenActiveSerializer . Deserialize < OrderProposal > ( orderJson ) ;
444443 if ( order == null || order . GetType ( ) != typeof ( OrderProposal ) )
445444 {
@@ -448,14 +447,47 @@ public async Task<ResponseContent> ProcessOrderProposalCreationP(string clientId
448447 var ( orderId , sellerIdComponents , seller , customerAccountIdComponents ) = await ConstructIdsFromRequest ( clientId , sellerId , customerAccountId , uuidString , OrderType . OrderProposal ) ;
449448 using ( await asyncDuplicateLock . LockAsync ( GetParallelLockKey ( orderId ) ) )
450449 {
450+ // Attempt to use idempotency cache if it exists
451+ var cachedResponse = await GetResponseFromIdempotencyStoreIfExists ( settings , orderId , orderJson ) ;
452+ if ( cachedResponse != null )
453+ {
454+ return cachedResponse ;
455+ }
456+
451457 var response = await ProcessFlowRequest ( ValidateFlowRequest < OrderProposal > ( orderId , sellerIdComponents , seller , customerAccountIdComponents , FlowStage . P , order ) , order ) ;
452458
453- // Return a 409 status code if any OrderItem level errors exist
454- return ResponseContent . OpenBookingResponse ( OpenActiveSerializer . Serialize ( response ) ,
455- response . OrderedItem . Exists ( x => x . Error ? . Count > 0 ) ? HttpStatusCode . Conflict : HttpStatusCode . Created ) ;
459+ return await CreateResponseViaIdempotencyStoreIfExists ( settings , orderId , orderJson , response ) ;
456460 }
457461 }
458462
463+ private async Task < ResponseContent > GetResponseFromIdempotencyStoreIfExists ( BookingEngineSettings settings , OrderIdComponents orderId , string orderJson )
464+ {
465+ // Attempt to use idempotency cache if it exists
466+ if ( settings . IdempotencyStore != null )
467+ {
468+ var cachedResponse = await settings . IdempotencyStore . GetSuccessfulOrderCreationResponse ( orderId , orderJson ) ;
469+ if ( cachedResponse != null )
470+ {
471+ return ResponseContent . OpenBookingResponse ( cachedResponse , HttpStatusCode . Created ) ;
472+ }
473+ }
474+ return null ;
475+ }
476+
477+ private async Task < ResponseContent > CreateResponseViaIdempotencyStoreIfExists ( BookingEngineSettings settings , OrderIdComponents orderId , string orderJson , Order response ) {
478+ // Return a 409 status code if any OrderItem level errors exist
479+ var httpStatusCode = response . OrderedItem . Exists ( x => x . Error ? . Count > 0 ) ? HttpStatusCode . Conflict : HttpStatusCode . Created ;
480+ var responseJson = OpenActiveSerializer . Serialize ( response ) ;
481+
482+ // Store response in idempotency cache if it exists, and if the response is successful
483+ if ( settings . IdempotencyStore != null && httpStatusCode == HttpStatusCode . Created )
484+ {
485+ await settings . IdempotencyStore . SetSuccessfulOrderCreationResponse ( orderId , orderJson , responseJson ) ;
486+ }
487+
488+ return ResponseContent . OpenBookingResponse ( responseJson , httpStatusCode ) ;
489+ }
490+
459491 private SimpleIdComponents GetSimpleIdComponentsFromApiKey ( Uri sellerId )
460492 {
461493 // Return empty SimpleIdComponents in Single Seller mode, as it is not required in the API Key
0 commit comments