88using System . Text . Json ;
99using System . Threading ;
1010using System . Threading . Tasks ;
11+ using Microsoft . AspNetCore . Http ;
1112using Umbraco . Commerce . Common . Logging ;
1213using Umbraco . Commerce . Core . Api ;
1314using Umbraco . Commerce . Core . Models ;
@@ -36,14 +37,14 @@ public QuickpayCheckoutPaymentProvider(UmbracoCommerceContext ctx, ILogger<Quick
3637
3738 public override IEnumerable < TransactionMetaDataDefinition > TransactionMetaDataDefinitions => new [ ]
3839 {
39- new TransactionMetaDataDefinition ( "quickPayOrderId " , "Quickpay Order ID" ) ,
40- new TransactionMetaDataDefinition ( "quickPayPaymentId " , "Quickpay Payment ID" ) ,
41- new TransactionMetaDataDefinition ( "quickPayPaymentHash " , "Quickpay Payment Hash" )
40+ new TransactionMetaDataDefinition ( "quickpayOrderId " , "Quickpay Order ID" ) ,
41+ new TransactionMetaDataDefinition ( "quickpayPaymentId " , "Quickpay Payment ID" ) ,
42+ new TransactionMetaDataDefinition ( "quickpayPaymentHash " , "Quickpay Payment Hash" )
4243 } ;
4344
4445 public override async Task < PaymentFormResult > GenerateFormAsync ( PaymentProviderContext < QuickpayCheckoutSettings > ctx , CancellationToken cancellationToken = default )
4546 {
46- var currency = Context . Services . CurrencyService . GetCurrency ( ctx . Order . CurrencyId ) ;
47+ var currency = await Context . Services . CurrencyService . GetCurrencyAsync ( ctx . Order . CurrencyId ) ;
4748 var currencyCode = currency . Code . ToUpperInvariant ( ) ;
4849
4950 // Ensure currency has valid ISO 4217 code
@@ -63,12 +64,12 @@ public override async Task<PaymentFormResult> GenerateFormAsync(PaymentProviderC
6364 // Parse language - default language is English.
6465 Enum . TryParse ( ctx . Settings . Lang , true , out QuickpayLang lang ) ;
6566
66- var quickPayOrderId = ctx . Order . Properties [ "quickPayOrderId " ] ? . Value ;
67- var quickPayPaymentId = ctx . Order . Properties [ "quickPayPaymentId " ] ? . Value ;
68- var quickPayPaymentHash = ctx . Order . Properties [ "quickPayPaymentHash " ] ? . Value ?? string . Empty ;
69- var quickPayPaymentLinkHash = ctx . Order . Properties [ "quickPayPaymentLinkHash " ] ? . Value ?? string . Empty ;
67+ var quickpayOrderId = ctx . Order . Properties [ "quickpayOrderId " ] ? . Value ;
68+ var quickpayPaymentId = ctx . Order . Properties [ "quickpayPaymentId " ] ? . Value ;
69+ var quickpayPaymentHash = ctx . Order . Properties [ "quickpayPaymentHash " ] ? . Value ?? string . Empty ;
70+ var quickpayPaymentLinkHash = ctx . Order . Properties [ "quickpayPaymentLinkHash " ] ? . Value ?? string . Empty ;
7071
71- if ( quickPayPaymentHash != GetPaymentHash ( quickPayPaymentId , ctx . Order . OrderNumber , currencyCode , orderAmount ) )
72+ if ( quickpayPaymentHash != GetPaymentHash ( quickpayPaymentId , ctx . Order . OrderNumber , currencyCode , orderAmount ) )
7273 {
7374 try
7475 {
@@ -82,7 +83,7 @@ public override async Task<PaymentFormResult> GenerateFormAsync(PaymentProviderC
8283 // Quickpay has a limit of order id between 4-20 characters.
8384 if ( reference . Length > 20 )
8485 {
85- var store = Context . Services . StoreService . GetStore ( ctx . Order . StoreId ) ;
86+ var store = await Context . Services . StoreService . GetStoreAsync ( ctx . Order . StoreId ) ;
8687 var orderNumberTemplate = store . OrderNumberTemplate ;
8788
8889 // If the order number template is not equals Vendr generated order number, we need to decide whether to trim prefix, suffix or both.
@@ -117,18 +118,18 @@ public override async Task<PaymentFormResult> GenerateFormAsync(PaymentProviderC
117118 { "orderNumber" , ctx . Order . OrderNumber }
118119 } ;
119120
120- quickPayOrderId = reference ;
121+ quickpayOrderId = reference ;
121122
122123 var payment = await client . CreatePaymentAsync (
123124 new QuickpayPaymentRequest
124125 {
125- OrderId = quickPayOrderId ,
126+ OrderId = quickpayOrderId ,
126127 Currency = currencyCode ,
127128 Variables = metaData
128129 } ,
129- cancellationToken ) . ConfigureAwait ( false ) ;
130+ cancellationToken ) ;
130131
131- quickPayPaymentId = GetTransactionId ( payment ) ;
132+ quickpayPaymentId = GetTransactionId ( payment ) ;
132133
133134 var paymentLink = await client . CreatePaymentLinkAsync ( payment . Id . ToString ( ) , new QuickpayPaymentLinkRequest
134135 {
@@ -143,12 +144,12 @@ public override async Task<PaymentFormResult> GenerateFormAsync(PaymentProviderC
143144 Framed = ctx . Settings . Framed
144145
145146 } ,
146- cancellationToken ) . ConfigureAwait ( false ) ;
147+ cancellationToken ) ;
147148
148149 paymentFormLink = paymentLink . Url ;
149150
150- quickPayPaymentHash = GetPaymentHash ( payment . Id . ToString ( ) , ctx . Order . OrderNumber , currencyCode , orderAmount ) ;
151- quickPayPaymentLinkHash = Base64Encode ( paymentFormLink ) ;
151+ quickpayPaymentHash = GetPaymentHash ( payment . Id . ToString ( ) , ctx . Order . OrderNumber , currencyCode , orderAmount ) ;
152+ quickpayPaymentLinkHash = Base64Encode ( paymentFormLink ) ;
152153 }
153154 catch ( Exception ex )
154155 {
@@ -158,17 +159,17 @@ public override async Task<PaymentFormResult> GenerateFormAsync(PaymentProviderC
158159 else
159160 {
160161 // Get payment link from order properties.
161- paymentFormLink = Base64Decode ( quickPayPaymentLinkHash ) ;
162+ paymentFormLink = Base64Decode ( quickpayPaymentLinkHash ) ;
162163 }
163164
164165 return new PaymentFormResult ( )
165166 {
166167 MetaData = new Dictionary < string , string >
167168 {
168- { "quickPayOrderId " , quickPayOrderId } ,
169- { "quickPayPaymentId " , quickPayPaymentId } ,
170- { "quickPayPaymentHash " , quickPayPaymentHash } ,
171- { "quickPayPaymentLinkHash " , quickPayPaymentLinkHash }
169+ { "quickpayOrderId " , quickpayOrderId } ,
170+ { "quickpayPaymentId " , quickpayPaymentId } ,
171+ { "quickpayPaymentHash " , quickpayPaymentHash } ,
172+ { "quickpayPaymentLinkHash " , quickpayPaymentLinkHash }
172173 } ,
173174 Form = new PaymentForm ( paymentFormLink , PaymentFormMethod . Get )
174175 } ;
@@ -179,15 +180,22 @@ public override async Task<CallbackResult> ProcessCallbackAsync(PaymentProviderC
179180 try
180181 {
181182 ArgumentNullException . ThrowIfNull ( context ) ;
182- if ( ! await ValidateChecksumAsync ( context . Request , context . Settings . PrivateKey , cancellationToken ) . ConfigureAwait ( false ) )
183+
184+ var webhookBody = await GetJsonBodyAsync ( context . HttpContext . Request , cancellationToken ) ;
185+
186+ if ( ! await ValidateChecksumAsync (
187+ context . HttpContext . Request . Headers [ "Quickpay-Checksum-Sha256" ] ,
188+ webhookBody ,
189+ context . Settings . PrivateKey ,
190+ cancellationToken ) )
183191 {
184192 Logger . Warn ( $ "Quickpay [{ context . Order . OrderNumber } ] - Checksum validation failed") ;
185193 return CallbackResult . BadRequest ( ) ;
186194 }
187195
188196 QuickpayPayment payment = await ParseCallbackAsync (
189- context . Request ,
190- cancellationToken ) . ConfigureAwait ( false ) ;
197+ webhookBody ,
198+ cancellationToken ) ;
191199
192200 if ( ! VerifyOrder ( context . Order , payment ) )
193201 {
@@ -251,7 +259,7 @@ private static bool VerifyOrder(OrderReadOnly order, QuickpayPayment payment)
251259 }
252260 else
253261 {
254- if ( order . Properties [ "quickPayOrderId " ] ? . Value == payment . OrderId )
262+ if ( order . Properties [ "quickpayOrderId " ] ? . Value == payment . OrderId )
255263 {
256264 return true ;
257265 }
@@ -271,7 +279,7 @@ public override async Task<ApiResult> FetchPaymentStatusAsync(PaymentProviderCon
271279 var clientConfig = GetQuickpayClientConfig ( ctx . Settings ) ;
272280 var client = new QuickpayClient ( clientConfig ) ;
273281
274- var payment = await client . GetPaymentAsync ( id , cancellationToken ) . ConfigureAwait ( false ) ;
282+ var payment = await client . GetPaymentAsync ( id , cancellationToken ) ;
275283
276284 Operation lastCompletedOperation = payment . Operations . LastOrDefault ( o => ! o . Pending && o . QuickpayStatusCode == "20000" ) ;
277285
@@ -308,7 +316,7 @@ public override async Task<ApiResult> CancelPaymentAsync(PaymentProviderContext<
308316 var clientConfig = GetQuickpayClientConfig ( ctx . Settings ) ;
309317 var client = new QuickpayClient ( clientConfig ) ;
310318
311- var payment = await client . CancelPaymentAsync ( id , cancellationToken ) . ConfigureAwait ( false ) ;
319+ var payment = await client . CancelPaymentAsync ( id , cancellationToken ) ;
312320
313321 Operation lastCompletedOperation = payment . Operations . LastOrDefault ( o => ! o . Pending && o . QuickpayStatusCode == "20000" ) ;
314322
@@ -349,7 +357,7 @@ public override async Task<ApiResult> CapturePaymentAsync(PaymentProviderContext
349357 {
350358 amount = AmountToMinorUnits ( ctx . Order . TransactionInfo . AmountAuthorized . Value )
351359 } ,
352- cancellationToken ) . ConfigureAwait ( false ) ;
360+ cancellationToken ) ;
353361
354362 Operation lastCompletedOperation = payment . Operations . LastOrDefault ( o => ! o . Pending && o . QuickpayStatusCode == "20000" ) ;
355363
@@ -390,7 +398,7 @@ public override async Task<ApiResult> RefundPaymentAsync(PaymentProviderContext<
390398 {
391399 amount = AmountToMinorUnits ( ctx . Order . TransactionInfo . AmountAuthorized . Value )
392400 } ,
393- cancellationToken ) . ConfigureAwait ( false ) ;
401+ cancellationToken ) ;
394402
395403 Operation lastCompletedOperation = payment . Operations . LastOrDefault ( o => ! o . Pending && o . QuickpayStatusCode == "20000" ) ;
396404
@@ -416,38 +424,28 @@ public override async Task<ApiResult> RefundPaymentAsync(PaymentProviderContext<
416424 return ApiResult . Empty ;
417425 }
418426
419- public async Task < QuickpayPayment > ParseCallbackAsync ( HttpRequestMessage request , CancellationToken cancellationToken = default )
427+ private async Task < string > GetJsonBodyAsync ( HttpRequest request , CancellationToken cancellationToken = default )
420428 {
421- using ( var stream = await request . Content . ReadAsStreamAsync ( cancellationToken ) . ConfigureAwait ( false ) )
429+ if ( request . Body . CanSeek )
422430 {
423- if ( stream . CanSeek )
424- {
425- stream . Seek ( 0 , SeekOrigin . Begin ) ;
426- }
427-
428- // Get quickpay callback body text - See parameters: https://learn.quickpay.net/tech-talk/api/callback/
429-
430- using ( var reader = new StreamReader ( stream ) )
431- {
432- var json = await reader . ReadToEndAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
433-
434- // Deserialize json body text
435- return JsonSerializer . Deserialize < QuickpayPayment > ( json ) ;
436- }
431+ request . Body . Seek ( 0 , SeekOrigin . Begin ) ;
437432 }
433+
434+ using var reader = new StreamReader ( request . Body ) ;
435+ return await reader . ReadToEndAsync ( cancellationToken ) ;
438436 }
439437
440- private async Task < bool > ValidateChecksumAsync ( HttpRequestMessage request , string privateAccountKey , CancellationToken cancellationToken = default )
441- {
442- var json = await request . Content . ReadAsStringAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
443- var checkSum = request . Headers . GetValues ( "Quickpay-Checksum-Sha256" ) . FirstOrDefault ( ) ;
438+ private Task < QuickpayPayment > ParseCallbackAsync ( string webhookBody , CancellationToken cancellationToken = default )
439+ => Task . FromResult ( JsonSerializer . Deserialize < QuickpayPayment > ( webhookBody ) ) ;
444440
445- if ( string . IsNullOrEmpty ( checkSum ) )
441+ private async Task < bool > ValidateChecksumAsync ( string checksum , string webhookBody , string privateAccountKey , CancellationToken cancellationToken = default )
442+ {
443+ if ( string . IsNullOrEmpty ( checksum ) )
446444 return false ;
447445
448- var calculatedChecksum = Checksum ( json , privateAccountKey ) ;
446+ var calculatedChecksum = Checksum ( webhookBody , privateAccountKey ) ;
449447
450- return checkSum . Equals ( calculatedChecksum ) ;
448+ return checksum . Equals ( calculatedChecksum ) ;
451449 }
452450
453451 private string Checksum ( string content , string privateKey )
0 commit comments