@@ -492,6 +492,200 @@ public async Task<Result<RecentActivityReceiptSearchResponse>> GetRecentActivity
492492 return response ;
493493 }
494494
495+ public async Task < Result < ResendReceiptResponse > > ResendReceipt ( Guid estateId ,
496+ Guid merchantId ,
497+ String reference ,
498+ String recipientEmailAddress ,
499+ CancellationToken cancellationToken )
500+ {
501+ Result < TokenResponse > accessTokenResult = await this . GetAccessToken ( cancellationToken ) ;
502+ if ( accessTokenResult . IsFailed ) {
503+ return ResultHelpers . CreateFailure ( accessTokenResult ) ;
504+ }
505+
506+ if ( string . IsNullOrWhiteSpace ( reference ) ) {
507+ return Result . Invalid ( "A transaction reference or receipt reference is required." ) ;
508+ }
509+
510+ if ( string . IsNullOrWhiteSpace ( recipientEmailAddress ) ) {
511+ return Result . Invalid ( "Recipient email address is required." ) ;
512+ }
513+
514+ try {
515+ _ = new System . Net . Mail . MailAddress ( recipientEmailAddress ) ;
516+ }
517+ catch ( FormatException ) {
518+ return Result . Invalid ( "Recipient email address is not valid." ) ;
519+ }
520+
521+ Result < ResendReceiptResponse > resendResult = await this . InvokeResendReceiptAsync ( accessTokenResult . Data . AccessToken ,
522+ estateId ,
523+ merchantId ,
524+ reference ,
525+ recipientEmailAddress ,
526+ cancellationToken ) ;
527+
528+ if ( resendResult . IsFailed ) {
529+ return resendResult ;
530+ }
531+
532+ return resendResult ;
533+ }
534+
535+ private async Task < Result < ResendReceiptResponse > > InvokeResendReceiptAsync ( String accessToken ,
536+ Guid estateId ,
537+ Guid merchantId ,
538+ String reference ,
539+ String recipientEmailAddress ,
540+ CancellationToken cancellationToken )
541+ {
542+ System . Reflection . MethodInfo ? method = typeof ( ITransactionProcessorClient )
543+ . GetMethods ( )
544+ . SingleOrDefault ( m => string . Equals ( m . Name , "ResendEmailReceipt" , StringComparison . Ordinal ) ) ;
545+
546+ if ( method == null ) {
547+ return Result . Failure ( "Receipt resend is not supported by the transaction client." ) ;
548+ }
549+
550+ System . Reflection . ParameterInfo [ ] parameters = method . GetParameters ( ) ;
551+ object ? [ ] args = new object ? [ parameters . Length ] ;
552+ bool estateAssigned = false ;
553+ bool merchantAssigned = false ;
554+ object ? request = null ;
555+
556+ for ( int index = 0 ; index < parameters . Length ; index ++ ) {
557+ System . Reflection . ParameterInfo parameter = parameters [ index ] ;
558+
559+ if ( parameter . ParameterType == typeof ( String ) ) {
560+ args [ index ] = accessToken ;
561+ continue ;
562+ }
563+
564+ if ( parameter . ParameterType == typeof ( Guid ) ) {
565+ if ( estateAssigned == false ) {
566+ args [ index ] = estateId ;
567+ estateAssigned = true ;
568+ }
569+ else if ( merchantAssigned == false ) {
570+ args [ index ] = merchantId ;
571+ merchantAssigned = true ;
572+ }
573+ else {
574+ args [ index ] = Guid . Empty ;
575+ }
576+
577+ continue ;
578+ }
579+
580+ if ( parameter . ParameterType == typeof ( CancellationToken ) ) {
581+ args [ index ] = cancellationToken ;
582+ continue ;
583+ }
584+
585+ request ??= System . Activator . CreateInstance ( parameter . ParameterType ) ;
586+ if ( request == null ) {
587+ return Result . Failure ( $ "Unable to create resend receipt request type '{ parameter . ParameterType . FullName } '.") ;
588+ }
589+
590+ PopulateResendReceiptRequest ( request , estateId , merchantId , reference , recipientEmailAddress ) ;
591+ args [ index ] = request ;
592+ }
593+
594+ object ? invocationResult = method . Invoke ( this . TransactionProcessorClient , args ) ;
595+ if ( invocationResult is not Task task ) {
596+ return Result . Failure ( "Receipt resend call did not return a task." ) ;
597+ }
598+
599+ await task . ConfigureAwait ( false ) ;
600+
601+ object ? taskResult = task . GetType ( ) . GetProperty ( "Result" ) ? . GetValue ( task ) ;
602+ if ( taskResult == null ) {
603+ return Result . Failure ( "Receipt resend call returned no result." ) ;
604+ }
605+
606+ bool isFailed = ( bool ) ( taskResult . GetType ( ) . GetProperty ( "IsFailed" ) ? . GetValue ( taskResult ) ?? true ) ;
607+ if ( isFailed ) {
608+ string ? message = taskResult . GetType ( ) . GetProperty ( "Message" ) ? . GetValue ( taskResult ) ? . ToString ( ) ;
609+ string ? status = taskResult . GetType ( ) . GetProperty ( "Status" ) ? . GetValue ( taskResult ) ? . ToString ( ) ;
610+
611+ if ( string . Equals ( status , "NotFound" , StringComparison . OrdinalIgnoreCase ) ) {
612+ return Result . NotFound ( message ?? "Receipt could not be found." ) ;
613+ }
614+
615+ if ( string . Equals ( status , "Invalid" , StringComparison . OrdinalIgnoreCase ) ) {
616+ return Result . Invalid ( message ?? "Receipt resend request was invalid." ) ;
617+ }
618+
619+ return Result . Failure ( message ?? "Receipt resend failed." ) ;
620+ }
621+
622+ object ? data = taskResult . GetType ( ) . GetProperty ( "Data" ) ? . GetValue ( taskResult ) ;
623+ return Result . Success ( MapResendReceiptResponse ( data , reference ) ) ;
624+ }
625+
626+ private static void PopulateResendReceiptRequest ( object request ,
627+ Guid estateId ,
628+ Guid merchantId ,
629+ String reference ,
630+ String recipientEmailAddress )
631+ {
632+ Type requestType = request . GetType ( ) ;
633+ SetIfPresent ( requestType , request , "EstateId" , estateId ) ;
634+ SetIfPresent ( requestType , request , "MerchantId" , merchantId ) ;
635+ SetIfPresent ( requestType , request , "Reference" , reference ) ;
636+ SetIfPresent ( requestType , request , "ReceiptReference" , reference ) ;
637+ SetIfPresent ( requestType , request , "TransactionReference" , reference ) ;
638+ SetIfPresent ( requestType , request , "RecipientEmail" , recipientEmailAddress ) ;
639+ SetIfPresent ( requestType , request , "RecipientEmailAddress" , recipientEmailAddress ) ;
640+ SetIfPresent ( requestType , request , "EmailAddress" , recipientEmailAddress ) ;
641+ }
642+
643+ private static void SetIfPresent ( Type requestType , object request , String propertyName , object value )
644+ {
645+ System . Reflection . PropertyInfo ? property = requestType . GetProperty ( propertyName , System . Reflection . BindingFlags . Instance | System . Reflection . BindingFlags . Public | System . Reflection . BindingFlags . IgnoreCase ) ;
646+ if ( property == null || property . CanWrite == false ) {
647+ return ;
648+ }
649+
650+ property . SetValue ( request , value ) ;
651+ }
652+
653+ private static ResendReceiptResponse MapResendReceiptResponse ( object ? data , String reference )
654+ {
655+ ResendReceiptResponse response = new ( )
656+ {
657+ Success = true ,
658+ Message = "Receipt resend requested." ,
659+ Reference = reference
660+ } ;
661+
662+ if ( data == null ) {
663+ return response ;
664+ }
665+
666+ Type responseType = data . GetType ( ) ;
667+ response . Message = GetStringProperty ( responseType , data , "Message" ) ?? response . Message ;
668+ response . Reference = GetStringProperty ( responseType , data , "Reference" ) ?? response . Reference ;
669+ response . ReceiptReference = GetStringProperty ( responseType , data , "ReceiptReference" ) ;
670+ response . TransactionReference = GetStringProperty ( responseType , data , "TransactionReference" ) ;
671+
672+ if ( string . IsNullOrWhiteSpace ( response . Reference ) && string . IsNullOrWhiteSpace ( response . ReceiptReference ) == false ) {
673+ response . Reference = response . ReceiptReference ;
674+ }
675+
676+ if ( string . IsNullOrWhiteSpace ( response . Reference ) && string . IsNullOrWhiteSpace ( response . TransactionReference ) == false ) {
677+ response . Reference = response . TransactionReference ;
678+ }
679+
680+ return response ;
681+ }
682+
683+ private static string ? GetStringProperty ( Type type , object instance , String propertyName )
684+ {
685+ System . Reflection . PropertyInfo ? property = type . GetProperty ( propertyName , System . Reflection . BindingFlags . Instance | System . Reflection . BindingFlags . Public | System . Reflection . BindingFlags . IgnoreCase ) ;
686+ return property ? . GetValue ( instance ) ? . ToString ( ) ;
687+ }
688+
495689 private static ProcessReconciliationResponse CreateProcessReconciliationResponse ( ReconciliationResponse reconciliationResponse )
496690 {
497691 return new ProcessReconciliationResponse
0 commit comments