/// <summary> /// Does the actual work of refunding a payment /// </summary> /// <param name="invoice">The <see cref="IInvoice"/></param> /// <param name="payment">The previously Authorize payment to be captured</param> /// <param name="amount">The amount to be refunded</param> /// <param name="args">Any arguments required to process the payment.</param> /// <returns>The <see cref="IPaymentResult"/></returns> protected override IPaymentResult PerformRefundPayment(IInvoice invoice, IPayment payment, decimal amount, ProcessorArgumentCollection args) { var result = _processor.RefundPayment(invoice, payment, amount); if (!result.Payment.Success) { GatewayProviderService.ApplyPaymentToInvoice(payment.Key, invoice.Key, AppliedPaymentType.Denied, result.Payment.Exception.Message, 0); return(result); } // use the overloaded AppliedPayments method here for testing if we don't have // a MerchelloContext foreach (var applied in payment.AppliedPayments(GatewayProviderService)) { applied.TransactionType = AppliedPaymentType.Refund; applied.Amount = 0; applied.Description += " - Refunded"; GatewayProviderService.Save(applied); } payment.Amount = payment.Amount - amount; if (payment.Amount != 0) { GatewayProviderService.ApplyPaymentToInvoice(payment.Key, invoice.Key, AppliedPaymentType.Debit, "To show partial payment remaining after refund", payment.Amount); } GatewayProviderService.Save(payment); return(new PaymentResult(Attempt <IPayment> .Succeed(payment), invoice, false)); }
/// <summary> /// Does the actual work of voiding a payment /// </summary> /// <param name="invoice">The invoice to which the payment is associated</param> /// <param name="payment">The payment to be voided</param> /// <param name="args">Additional arguements required by the payment processor</param> /// <returns>A <see cref="IPaymentResult"/></returns> protected override IPaymentResult PerformVoidPayment(IInvoice invoice, IPayment payment, ProcessorArgumentCollection args) { var result = _processor.VoidPayment(invoice, payment); if (!result.Payment.Success) { GatewayProviderService.ApplyPaymentToInvoice(payment.Key, invoice.Key, AppliedPaymentType.Denied, result.Payment.Exception.Message, 0); return result; } // use the overloaded AppliedPayments method here for testing if we don't have // a MerchelloContext foreach (var applied in payment.AppliedPayments(GatewayProviderService)) { applied.TransactionType = AppliedPaymentType.Refund; applied.Amount = 0; applied.Description += " - **Void**"; GatewayProviderService.Save(applied); } payment.Voided = true; GatewayProviderService.Save(payment); return new PaymentResult(Attempt<IPayment>.Succeed(payment), invoice, false); }
/// <summary> /// The perform void payment. /// </summary> /// <param name="invoice"> /// The invoice. /// </param> /// <param name="payment"> /// The payment. /// </param> /// <param name="args"> /// The args. /// </param> /// <returns> /// The <see cref="IPaymentResult"/>. /// </returns> protected override IPaymentResult PerformVoidPayment(IInvoice invoice, IPayment payment, ProcessorArgumentCollection args) { string transactionString; if (args.TryGetValue(Constants.Braintree.ExtendedDataKeys.BraintreeTransaction, out transactionString)) { var transaction = JsonConvert.DeserializeObject <Transaction>(transactionString); var result = this._braintreeApiService.Transaction.Refund(transaction.Id); if (!result.IsSuccess()) { return(new PaymentResult(Attempt <IPayment> .Fail(payment), invoice, false)); } foreach (var applied in payment.AppliedPayments()) { applied.TransactionType = AppliedPaymentType.Void; applied.Amount = 0; applied.Description += " - **Void**"; this.GatewayProviderService.Save(applied); } payment.Voided = true; this.GatewayProviderService.Save(payment); return(new PaymentResult(Attempt <IPayment> .Succeed(payment), invoice, false)); } return(new PaymentResult(Attempt <IPayment> .Fail(payment), invoice, false)); }
/// <summary> /// Voids a payment /// </summary> /// <param name="invoice">The invoice associated with the payment to be voided</param> /// <param name="payment">The payment to be voided</param> /// <param name="args">Additional arguments required by the payment processor</param> /// <returns>A <see cref="IPaymentResult"/></returns> public virtual IPaymentResult VoidPayment(IInvoice invoice, IPayment payment, ProcessorArgumentCollection args) { Mandate.ParameterNotNull(invoice, "invoice"); if (!invoice.HasIdentity) { return(new PaymentResult(Attempt <IPayment> .Fail(new InvalidOperationException("Cannot void a payment on an invoice that cannot have payments")), invoice, false)); } var operationData = new PaymentOperationData() { Invoice = invoice, Payment = payment, PaymentMethod = this.PaymentMethod, ProcessorArgumentCollection = args }; Voiding.RaiseEvent(new SaveEventArgs <PaymentOperationData>(operationData), this); var response = PerformVoidPayment(invoice, payment, args); VoidAttempted.RaiseEvent(new PaymentAttemptEventArgs <IPaymentResult>(response), this); if (!response.Payment.Success) { return(response); } var appliedPayments = payment.AppliedPayments().Where(x => x.TransactionType != AppliedPaymentType.Void); foreach (var appliedPayment in appliedPayments) { appliedPayment.TransactionType = AppliedPaymentType.Void; appliedPayment.Amount = 0; GatewayProviderService.Save(appliedPayment); } // Assert the payment has been voided if (!payment.Voided) { payment.Voided = true; GatewayProviderService.Save(payment); } AssertInvoiceStatus(response.Invoice); // Force the ApproveOrderCreation to false if (response.ApproveOrderCreation) { ((PaymentResult)response).ApproveOrderCreation = false; } // give response return(response); }
/// <summary> /// Performs a refund or a partial refund. /// </summary> /// <param name="invoice"> /// The invoice. /// </param> /// <param name="payment"> /// The payment. /// </param> /// <param name="amount"> /// The amount. /// </param> /// <param name="args"> /// The processor arguments. /// </param> /// <returns> /// The <see cref="IPaymentResult"/>. /// </returns> protected override IPaymentResult PerformRefundPayment(IInvoice invoice, IPayment payment, decimal amount, ProcessorArgumentCollection args) { var record = payment.GetPayPalTransactionRecord(); if (StringExtensions.IsNullOrWhiteSpace(record.Data.CaptureTransactionId)) { var error = new NullReferenceException("PayPal transaction could not be found and/or deserialized from payment extended data collection"); return(new PaymentResult(Attempt <IPayment> .Fail(payment, error), invoice, false)); } var attempt = _paypalApiService.ExpressCheckout.Refund(invoice, payment, amount); // store the transaction var refundTransActions = record.RefundTransactions.ToList(); refundTransActions.Add(attempt); record.RefundTransactions = refundTransActions; if (!attempt.Success()) { // In the case of a failure, package up the exception so we can bubble it up. var ex = new PayPalApiException("PayPal Checkout Express refund response ACK was not Success"); if (record.SetExpressCheckout.ErrorTypes.Any()) { ex.ErrorTypes = record.SetExpressCheckout.ErrorTypes; } // ensure that transaction is stored in the payment payment.SavePayPalTransactionRecord(record); GatewayProviderService.Save(payment); return(new PaymentResult(Attempt <IPayment> .Fail(payment, ex), invoice, false)); } foreach (var applied in payment.AppliedPayments()) { applied.TransactionType = AppliedPaymentType.Refund; applied.Amount = 0; applied.Description += " - Refunded"; this.GatewayProviderService.Save(applied); } payment.Amount = payment.Amount - amount; if (payment.Amount != 0) { this.GatewayProviderService.ApplyPaymentToInvoice(payment.Key, invoice.Key, AppliedPaymentType.Debit, "To show partial payment remaining after refund", payment.Amount); } this.GatewayProviderService.Save(payment); return(new PaymentResult(Attempt <IPayment> .Succeed(payment), invoice, false)); }
/// <summary> /// Does the actual work of voiding a payment /// </summary> /// <param name="invoice">The invoice to which the payment is associated</param> /// <param name="payment">The payment to be voided</param> /// <param name="args">Additional arguments required by the payment processor</param> /// <returns>A <see cref="IPaymentResult"/></returns> protected override IPaymentResult PerformVoidPayment(IInvoice invoice, IPayment payment, ProcessorArgumentCollection args) { foreach (var applied in payment.AppliedPayments()) { applied.TransactionType = AppliedPaymentType.Void; applied.Amount = 0; applied.Description += " - **Void**"; GatewayProviderService.Save(applied); } payment.Voided = true; GatewayProviderService.Save(payment); return(new PaymentResult(Attempt <IPayment> .Succeed(payment), invoice, false)); }
/// <summary> /// Does the actual work of refunding a payment /// </summary> /// <param name="invoice">The <see cref="IInvoice"/></param> /// <param name="payment">The previously Authorize payment to be captured</param> /// <param name="amount">The amount to be refunded</param> /// <param name="args">Any arguments required to process the payment.</param> /// <returns>The <see cref="IPaymentResult"/></returns> protected override IPaymentResult PerformRefundPayment(IInvoice invoice, IPayment payment, decimal amount, ProcessorArgumentCollection args) { foreach (var applied in payment.AppliedPayments()) { applied.TransactionType = AppliedPaymentType.Refund; applied.Amount = 0; applied.Description += " - Refunded"; GatewayProviderService.Save(applied); } payment.Amount = payment.Amount - amount; if (payment.Amount != 0) { GatewayProviderService.ApplyPaymentToInvoice(payment.Key, invoice.Key, AppliedPaymentType.Debit, "To show partial payment remaining after refund", payment.Amount); } GatewayProviderService.Save(payment); return(new PaymentResult(Attempt <IPayment> .Succeed(payment), invoice, false)); }
/// <summary> /// Performs the actual work of performing the refund. /// </summary> /// <param name="invoice"> /// The invoice. /// </param> /// <param name="payment"> /// The payment. /// </param> /// <param name="amount"> /// The amount. /// </param> /// <param name="args"> /// The args. /// </param> /// <returns> /// The <see cref="IPaymentResult"/>. /// </returns> protected override IPaymentResult PerformRefundPayment(IInvoice invoice, IPayment payment, decimal amount, ProcessorArgumentCollection args) { var transaction = payment.ExtendedData.GetBraintreeTransaction(); if (transaction == null) { var error = new NullReferenceException("Braintree transaction could not be found and/or deserialized from payment extended data collection"); return(new PaymentResult(Attempt <IPayment> .Fail(payment, error), invoice, false)); } var attempt = this.BraintreeApiService.Transaction.Refund(transaction.Id, amount); if (!attempt.IsSuccess()) { var error = new BraintreeApiException(attempt.Errors, attempt.Message); this.GatewayProviderService.ApplyPaymentToInvoice(payment.Key, invoice.Key, AppliedPaymentType.Refund, error.Message, 0); return(new PaymentResult(Attempt <IPayment> .Fail(payment, error), invoice, false)); } foreach (var applied in payment.AppliedPayments()) { applied.TransactionType = AppliedPaymentType.Refund; applied.Amount = 0; applied.Description += " - Refunded"; this.GatewayProviderService.Save(applied); } payment.Amount = payment.Amount - amount; if (payment.Amount != 0) { this.GatewayProviderService.ApplyPaymentToInvoice(payment.Key, invoice.Key, AppliedPaymentType.Debit, "To show partial payment remaining after refund", payment.Amount); } this.GatewayProviderService.Save(payment); return(new PaymentResult(Attempt <IPayment> .Succeed(payment), invoice, false)); }
/// <summary> /// Refunds a payment /// </summary> /// <param name="invoice">The invoice to be the payment was applied</param> /// <param name="payment">The payment to be refunded</param> /// <param name="amount">The amount to be refunded</param> /// <param name="args">Additional arguments required by the payment processor</param> /// <returns>A <see cref="IPaymentResult"/></returns> public virtual IPaymentResult RefundPayment(IInvoice invoice, IPayment payment, decimal amount, ProcessorArgumentCollection args) { Mandate.ParameterNotNull(invoice, "invoice"); if (!invoice.HasIdentity) { return(new PaymentResult(Attempt <IPayment> .Fail(new InvalidOperationException("Cannot refund a payment on an invoice that cannot have payments")), invoice, false)); } var response = PerformRefundPayment(invoice, payment, amount, args); RefundAttempted.RaiseEvent(new PaymentAttemptEventArgs <IPaymentResult>(response), this); if (!response.Payment.Success) { return(response); } var appliedPayments = payment.AppliedPayments().Where(x => x.TransactionType != AppliedPaymentType.Void); foreach (var appliedPayment in appliedPayments) { appliedPayment.TransactionType = AppliedPaymentType.Void; appliedPayment.Amount = 0; GatewayProviderService.Save(appliedPayment); } AssertInvoiceStatus(invoice); // Force the ApproveOrderCreation to false if (response.ApproveOrderCreation) { ((PaymentResult)response).ApproveOrderCreation = false; } // give response return(response); }
/// <summary> /// Performs a refund or a partial refund. /// </summary> /// <param name="invoice"> /// The invoice. /// </param> /// <param name="payment"> /// The payment. /// </param> /// <param name="amount"> /// The amount. /// </param> /// <param name="args"> /// The processor arguments. /// </param> /// <returns> /// The <see cref="IPaymentResult"/>. /// </returns> protected override IPaymentResult PerformRefundPayment(IInvoice invoice, IPayment payment, decimal amount, ProcessorArgumentCollection args) { var record = payment.GetPayPalTransactionRecord(); if (StringExtensions.IsNullOrWhiteSpace(record.Data.CaptureTransactionId)) { var error = new NullReferenceException("PayPal transaction could not be found and/or deserialized from payment extended data collection"); return new PaymentResult(Attempt<IPayment>.Fail(payment, error), invoice, false); } var attempt = _paypalApiService.ExpressCheckout.Refund(invoice, payment, amount); // store the transaction var refundTransActions = record.RefundTransactions.ToList(); refundTransActions.Add(attempt); record.RefundTransactions = refundTransActions; if (!attempt.Success()) { // In the case of a failure, package up the exception so we can bubble it up. var ex = new PayPalApiException("PayPal Checkout Express refund response ACK was not Success"); if (record.SetExpressCheckout.ErrorTypes.Any()) ex.ErrorTypes = record.SetExpressCheckout.ErrorTypes; // ensure that transaction is stored in the payment payment.SavePayPalTransactionRecord(record); GatewayProviderService.Save(payment); return new PaymentResult(Attempt<IPayment>.Fail(payment, ex), invoice, false); } foreach (var applied in payment.AppliedPayments()) { applied.TransactionType = AppliedPaymentType.Refund; applied.Amount = 0; applied.Description += " - Refunded"; this.GatewayProviderService.Save(applied); } payment.Amount = payment.Amount - amount; if (payment.Amount != 0) { this.GatewayProviderService.ApplyPaymentToInvoice(payment.Key, invoice.Key, AppliedPaymentType.Debit, "To show partial payment remaining after refund", payment.Amount); } this.GatewayProviderService.Save(payment); return new PaymentResult(Attempt<IPayment>.Succeed(payment), invoice, false); }
/// <summary> /// Returns a collection of <see cref="IAppliedPayment"/> for this <see cref="IPayment"/> /// </summary> /// <param name="payment">The <see cref="IPayment"/></param> /// <returns>A collection of <see cref="IAppliedPayment"/></returns> public static IEnumerable <IAppliedPayment> AppliedPayments(this IPayment payment) { return(payment.AppliedPayments(MerchelloContext.Current)); }
/// <summary> /// Refunds a payment /// </summary> /// <param name="invoice">The invoice to be the payment was applied</param> /// <param name="payment">The payment to be refunded</param> /// <param name="args">Additional arguements required by the payment processor</param> /// <returns>A <see cref="IPaymentResult"/></returns> public IPaymentResult RefundPayment(IInvoice invoice, IPayment payment, ProcessorArgumentCollection args) { Mandate.ParameterNotNull(invoice, "invoice"); if(!invoice.HasIdentity) return new PaymentResult(Attempt<IPayment>.Fail(new InvalidOperationException("Cannot refund a payment on an invoice that cannot have payments")), invoice, false); var response = PerformRefundPayment(invoice, payment, args); if (!response.Payment.Success) return response; var appliedPayments = payment.AppliedPayments().Where(x => x.TransactionType != AppliedPaymentType.Void); foreach (var appliedPayment in appliedPayments) { appliedPayment.TransactionType = AppliedPaymentType.Void; appliedPayment.Amount = 0; GatewayProviderService.Save(appliedPayment); } AssertInvoiceStatus(invoice); // Force the ApproveOrderCreation to false if (response.ApproveOrderCreation) ((PaymentResult)response).ApproveOrderCreation = false; // give response return response; }
/// <summary> /// Returns a collection of <see cref="IAppliedPayment"/> for this <see cref="IPayment"/> /// </summary> /// <param name="payment">The <see cref="IPayment"/></param> /// <param name="merchelloContext">The <see cref="IMerchelloContext"/></param> /// <returns>A collection of <see cref="IAppliedPayment"/></returns> internal static IEnumerable <IAppliedPayment> AppliedPayments(this IPayment payment, IMerchelloContext merchelloContext) { return(payment.AppliedPayments(merchelloContext.Services.GatewayProviderService)); }
/// <summary> /// Performs the actual work of voiding a payment in Merchello ONLY /// </summary> /// <param name="invoice"> /// The invoice. /// </param> /// <param name="payment"> /// The payment. /// </param> /// <param name="args"> /// The args. /// </param> /// <returns> /// The <see cref="IPaymentResult"/>. /// </returns> /// <remarks> /// This merely VOIDs the payment in Merchello and does nothing against the Braintree API as Braintree does not do a VOID /// </remarks> protected override IPaymentResult PerformVoidPayment(IInvoice invoice, IPayment payment, ProcessorArgumentCollection args) { payment.Voided = true; payment.Amount = 0; foreach (var applied in payment.AppliedPayments(GatewayProviderService)) { applied.TransactionType = AppliedPaymentType.Void; applied.Amount = 0; applied.Description += " - Voided"; GatewayProviderService.Save(applied); } GatewayProviderService.Save(payment); return new PaymentResult(Attempt<IPayment>.Succeed(payment), invoice, false); }
/// <summary> /// The perform refund payment. /// </summary> /// <param name="invoice"> /// The invoice. /// </param> /// <param name="payment"> /// The payment. /// </param> /// <param name="amount"> /// The amount. /// </param> /// <param name="args"> /// The args. /// </param> /// <returns> /// The <see cref="IPaymentResult"/>. /// </returns> protected override IPaymentResult PerformRefundPayment(IInvoice invoice, IPayment payment, decimal amount, ProcessorArgumentCollection args) { foreach (var applied in payment.AppliedPayments()) { applied.TransactionType = AppliedPaymentType.Refund; applied.Amount = 0; applied.Description += " - Refunded"; GatewayProviderService.Save(applied); } payment.Amount = payment.Amount - amount; if (payment.Amount != 0) { GatewayProviderService.ApplyPaymentToInvoice(payment.Key, invoice.Key, AppliedPaymentType.Debit, "To show partial payment remaining after refund", payment.Amount); } GatewayProviderService.Save(payment); return new PaymentResult(Attempt<IPayment>.Succeed(payment), invoice, false); }
/// <summary> /// Voids a payment /// </summary> /// <param name="invoice">The invoice associated with the payment to be voided</param> /// <param name="payment">The payment to be voided</param> /// <param name="args">Additional arguments required by the payment processor</param> /// <returns>A <see cref="IPaymentResult"/></returns> public virtual IPaymentResult VoidPayment(IInvoice invoice, IPayment payment, ProcessorArgumentCollection args) { Mandate.ParameterNotNull(invoice, "invoice"); if (!invoice.HasIdentity) return new PaymentResult(Attempt<IPayment>.Fail(new InvalidOperationException("Cannot void a payment on an invoice that cannot have payments")), invoice, false); var operationData = new PaymentOperationData() { Invoice = invoice, Payment = payment, ProcessorArgumentCollection = args }; Voiding.RaiseEvent(new SaveEventArgs<PaymentOperationData>(operationData), this); var response = PerformVoidPayment(invoice, payment, args); VoidAttempted.RaiseEvent(new PaymentAttemptEventArgs<IPaymentResult>(response), this); if (!response.Payment.Success) return response; var appliedPayments = payment.AppliedPayments().Where(x => x.TransactionType != AppliedPaymentType.Void); foreach (var appliedPayment in appliedPayments) { appliedPayment.TransactionType = AppliedPaymentType.Void; appliedPayment.Amount = 0; GatewayProviderService.Save(appliedPayment); } // Assert the payment has been voided if (!payment.Voided) { payment.Voided = true; GatewayProviderService.Save(payment); } AssertInvoiceStatus(invoice); // Force the ApproveOrderCreation to false if (response.ApproveOrderCreation) ((PaymentResult)response).ApproveOrderCreation = false; // give response return response; }
/// <summary> /// Performs the actual work of performing the refund. /// </summary> /// <param name="invoice"> /// The invoice. /// </param> /// <param name="payment"> /// The payment. /// </param> /// <param name="amount"> /// The amount. /// </param> /// <param name="args"> /// The args. /// </param> /// <returns> /// The <see cref="IPaymentResult"/>. /// </returns> protected override IPaymentResult PerformRefundPayment(IInvoice invoice, IPayment payment, decimal amount, ProcessorArgumentCollection args) { var transaction = payment.ExtendedData.GetBraintreeTransaction(); if (transaction == null) { var error = new NullReferenceException("Braintree transaction could not be found and/or deserialized from payment extended data collection"); return new PaymentResult(Attempt<IPayment>.Fail(payment, error), invoice, false); } var attempt = this.BraintreeApiService.Transaction.Refund(transaction.Id, amount); if (!attempt.IsSuccess()) { var error = new BraintreeApiException(attempt.Errors, attempt.Message); this.GatewayProviderService.ApplyPaymentToInvoice(payment.Key, invoice.Key, AppliedPaymentType.Refund, error.Message, 0); return new PaymentResult(Attempt<IPayment>.Fail(payment, error), invoice, false); } foreach (var applied in payment.AppliedPayments()) { applied.TransactionType = AppliedPaymentType.Refund; applied.Amount = 0; applied.Description += " - Refunded"; this.GatewayProviderService.Save(applied); } payment.Amount = payment.Amount - amount; if (payment.Amount != 0) { this.GatewayProviderService.ApplyPaymentToInvoice(payment.Key, invoice.Key, AppliedPaymentType.Debit, "To show partial payment remaining after refund", payment.Amount); } this.GatewayProviderService.Save(payment); return new PaymentResult(Attempt<IPayment>.Succeed(payment), invoice, false); }
/// <summary> /// The perform void payment. /// </summary> /// <param name="invoice"> /// The invoice. /// </param> /// <param name="payment"> /// The payment. /// </param> /// <param name="args"> /// The args. /// </param> /// <returns> /// The <see cref="IPaymentResult"/>. /// </returns> protected override IPaymentResult PerformVoidPayment(IInvoice invoice, IPayment payment, ProcessorArgumentCollection args) { string transactionString; if (args.TryGetValue(Constants.ExtendedDataKeys.BraintreeTransaction, out transactionString)) { var transaction = JsonConvert.DeserializeObject<Transaction>(transactionString); var result = _braintreeApiService.Transaction.Refund(transaction.Id); if (!result.IsSuccess()) return new PaymentResult(Attempt<IPayment>.Fail(payment), invoice, false); foreach (var applied in payment.AppliedPayments()) { applied.TransactionType = AppliedPaymentType.Void; applied.Amount = 0; applied.Description += " - **Void**"; GatewayProviderService.Save(applied); } payment.Voided = true; GatewayProviderService.Save(payment); return new PaymentResult(Attempt<IPayment>.Succeed(payment), invoice, false); } return new PaymentResult(Attempt<IPayment>.Fail(payment), invoice, false); }