/// <summary>
        /// Validates a successful response.
        /// </summary>
        /// <param name="invoice">
        /// The invoice.
        /// </param>
        /// <param name="payment">
        /// The payment.
        /// </param>
        /// <param name="token">
        /// The token.
        /// </param>
        /// <param name="payerId">
        /// The payer id.
        /// </param>
        /// <param name="record">
        /// The record.
        /// </param>
        /// <returns>
        /// The <see cref="ExpressCheckoutResponse"/>.
        /// </returns>
        /// <remarks>
        /// PayPal returns to the success URL even if the payment was declined. e.g.  The success URL represents a successful transaction
        /// not a successful payment so we need to do another request to verify the payment was completed and get additional information
        /// such as the transaction id so we can do refunds etc.
        /// </remarks>
        internal PayPalExpressTransactionRecord Authorize(IInvoice invoice, IPayment payment, string token, string payerId, PayPalExpressTransactionRecord record)
        {
            // Now we have to get the transaction details for the successful payment
            ExpressCheckoutResponse result = null;

            try
            {
                // do authorization
                var service = GetPayPalService();
                var doAuthorizationResponse = service.DoAuthorization(new DoAuthorizationReq
                {
                    DoAuthorizationRequest = new DoAuthorizationRequestType
                    {
                        TransactionID = record.Data.CheckoutPaymentTransactionId,
                        Amount        = new BasicAmountType(PayPalApiHelper.GetPayPalCurrencyCode(record.Data.CurrencyId), record.Data.AuthorizedAmount)
                    }
                });

                result = _responseFactory.Build(doAuthorizationResponse, token);
                if (result.Success())
                {
                    record.Data.Authorized = true;
                    record.Data.AuthorizationTransactionId = doAuthorizationResponse.TransactionID;
                }
            }
            catch (Exception ex)
            {
                result = _responseFactory.Build(ex);
            }

            record.DoAuthorization = result;

            return(record);
        }
示例#2
0
        /// <summary>
        /// Process payment when user checkout.
        /// </summary>
        /// <param name="cart">The current cart.</param>
        /// <param name="payment">The payment to process.</param>
        /// <returns>return false and set the message will make the WorkFlow activity raise PaymentExcetion(message)</returns>
        private PaymentProcessingResult ProcessPaymentCheckout(ICart cart, IPayment payment)
        {
            var orderNumberId = _orderNumberGenerator.GenerateOrderNumber(cart);

            if (string.IsNullOrEmpty(_paymentMethodConfiguration.ExpChkoutUrl) || string.IsNullOrEmpty(_paymentMethodConfiguration.PaymentAction))
            {
                return(PaymentProcessingResult.CreateUnsuccessfulResult(Utilities.Translate("PayPalSettingsError")));
            }

            var caller = PayPalApiHelper.GetPayPalApiCallerServices(_paymentMethodConfiguration);
            var setExpressChkOutReqType = new SetExpressCheckoutRequestType(SetupExpressCheckoutReqDetailsType(cart, payment, orderNumberId));
            var setChkOutResponse       = caller.SetExpressCheckout(new SetExpressCheckoutReq {
                SetExpressCheckoutRequest = setExpressChkOutReqType
            });
            var errorCheck = _payPalApiHelper.CheckErrors(setChkOutResponse);

            if (!string.IsNullOrEmpty(errorCheck))
            {
                _logger.Error(errorCheck);
                return(PaymentProcessingResult.CreateUnsuccessfulResult(string.Join("; ", setChkOutResponse.Errors.Select(e => e.LongMessage))));
            }

            payment.Properties[PayPalOrderNumberPropertyName] = orderNumberId;
            payment.Properties[PayPalExpTokenPropertyName]    = setChkOutResponse.Token;

            _orderRepository.Save(cart);

            // validation checking with PayPal OK (Server's PayPal API, Billing Address, Shipping Address, ... do redirect to PayPal.com
            var redirectUrl = CreateRedirectUrl(_paymentMethodConfiguration.ExpChkoutUrl, setChkOutResponse.Token);
            var message     = $"---PayPal-SetExpressCheckout is successful. Redirect end user to {redirectUrl}";

            _logger.Information(message);

            return(PaymentProcessingResult.CreateSuccessfulResult(message, redirectUrl));
        }
示例#3
0
        /// <summary>
        /// Builds the <see cref="PaymentDetailsItemType"/>.
        /// </summary>
        /// <param name="invoice">
        /// The invoice.
        /// </param>
        /// <param name="actionCode">
        /// The <see cref="PaymentActionCodeType"/>.
        /// </param>
        /// <returns>
        /// The <see cref="PaymentDetailsType"/>.
        /// </returns>
        public PaymentDetailsType Build(IInvoice invoice, PaymentActionCodeType actionCode)
        {
            // Get the decimal configuration for the current currency
            var currencyCodeType   = PayPalApiHelper.GetPayPalCurrencyCode(invoice.CurrencyCode);
            var basicAmountFactory = new PayPalBasicAmountTypeFactory(currencyCodeType);

            // Get the tax total
            var itemTotal     = basicAmountFactory.Build(invoice.TotalItemPrice());
            var shippingTotal = basicAmountFactory.Build(invoice.TotalShipping());
            var taxTotal      = basicAmountFactory.Build(invoice.TotalTax());
            var invoiceTotal  = basicAmountFactory.Build(invoice.Total);

            var items = BuildPaymentDetailsItemTypes(invoice.ProductLineItems(), basicAmountFactory);

            var paymentDetails = new PaymentDetailsType
            {
                PaymentDetailsItem = items.ToList(),
                ItemTotal          = itemTotal,
                TaxTotal           = taxTotal,
                ShippingTotal      = shippingTotal,
                OrderTotal         = invoiceTotal,
                PaymentAction      = actionCode,
                InvoiceID          = invoice.PrefixedInvoiceNumber()
            };

            // ShipToAddress
            if (invoice.ShippingLineItems().Any())
            {
                var addressTypeFactory = new PayPalAddressTypeFactory();
                paymentDetails.ShipToAddress = addressTypeFactory.Build(invoice.GetShippingAddresses().FirstOrDefault());
            }

            return(paymentDetails);
        }
 /// <summary>
 /// Initializes the factory.
 /// </summary>
 private void Initialize()
 {
     if (!this.WebsiteUrl.IsNullOrWhiteSpace())
     {
         return;
     }
     this.WebsiteUrl = PayPalApiHelper.GetBaseWebsiteUrl();
 }
        /// <summary>
        /// Refunds or partially refunds a payment.
        /// </summary>
        /// <param name="invoice">
        /// The invoice.
        /// </param>
        /// <param name="payment">
        /// The payment.
        /// </param>
        /// <param name="amount">
        /// The amount of the refund.
        /// </param>
        /// <returns>
        /// The <see cref="PayPalExpressTransactionRecord"/>.
        /// </returns>
        public ExpressCheckoutResponse Refund(IInvoice invoice, IPayment payment, decimal amount)
        {
            var record = payment.GetPayPalTransactionRecord();

            // Ensure the currency code
            if (record.Data.CurrencyCode.IsNullOrWhiteSpace())
            {
                var ex = new PayPalApiException("CurrencyCode was not found in payment extended data PayPal transaction data record.  Cannot perform refund.");
                return(_responseFactory.Build(ex));
            }

            // Ensure the transaction id
            if (record.Data.CaptureTransactionId.IsNullOrWhiteSpace())
            {
                var ex = new PayPalApiException("CaptureTransactionId was not found in payment extended data PayPal transaction data record.  Cannot perform refund.");
                return(_responseFactory.Build(ex));
            }

            // Get the decimal configuration for the current currency
            var currencyCodeType   = PayPalApiHelper.GetPayPalCurrencyCode(record.Data.CurrencyCode);
            var basicAmountFactory = new PayPalBasicAmountTypeFactory(currencyCodeType);

            ExpressCheckoutResponse result = null;

            if (amount > payment.Amount)
            {
                amount = payment.Amount;
            }

            try
            {
                var request = new RefundTransactionRequestType
                {
                    InvoiceID     = invoice.PrefixedInvoiceNumber(),
                    PayerID       = record.Data.PayerId,
                    RefundSource  = RefundSourceCodeType.DEFAULT,
                    Version       = record.DoCapture.Version,
                    TransactionID = record.Data.CaptureTransactionId,
                    Amount        = basicAmountFactory.Build(amount)
                };

                var wrapper = new RefundTransactionReq {
                    RefundTransactionRequest = request
                };

                var refundTransactionResponse = GetPayPalService().RefundTransaction(wrapper);

                result = _responseFactory.Build(refundTransactionResponse, record.Data.Token);
            }
            catch (Exception ex)
            {
                result = _responseFactory.Build(ex);
            }

            return(result);
        }
示例#6
0
        /// <summary>
        /// Captures an authorized payment.
        /// </summary>
        /// <para>
        /// See API doc here https://developer.paypal.com/webapps/developer/docs/classic/api/merchant/DoCapture_API_Operation_SOAP/
        /// </para>
        /// <param name="orderGroup">The order group to process.</param>
        /// <param name="payment">The payment to process</param>
        /// <returns>return false and set the message will make the WorkFlow activity raise PaymentExcetion(message)</returns>
        private PaymentProcessingResult ProcessPaymentCapture(IOrderGroup orderGroup, IPayment payment)
        {
            // Implement refund feature logic for current payment gateway
            var captureAmount = payment.Amount;

            if (!(orderGroup is IPurchaseOrder purchaseOrder) || captureAmount <= 0)
            {
                return(PaymentProcessingResult.CreateUnsuccessfulResult("Nothing to capture"));
            }

            var captureRequest = new DoCaptureRequestType
            {
                AuthorizationID = payment.TransactionID,                                               // original transactionID (which PayPal gave us when DoExpressCheckoutPayment, DoDirectPayment, or CheckOut)
                Amount          = _payPalApiHelper.ToPayPalAmount(captureAmount, orderGroup.Currency), // if refund with Partial, we have to set the Amount
                CompleteType    = payment.Amount >= purchaseOrder.GetTotal().Amount ? CompleteCodeType.COMPLETE : CompleteCodeType.NOTCOMPLETE,
                InvoiceID       = purchaseOrder.OrderNumber
            };

            captureRequest.Note = $"[{payment.PaymentMethodName}-{payment.TransactionID}] captured {captureAmount}{captureRequest.Amount.currencyID} for [PurchaseOrder-{purchaseOrder.OrderNumber}]";

            var caller       = PayPalApiHelper.GetPayPalApiCallerServices(_paymentMethodConfiguration);
            var doCaptureReq = new DoCaptureReq {
                DoCaptureRequest = captureRequest
            };
            var captureResponse = caller.DoCapture(doCaptureReq);

            var errorCheck = _payPalApiHelper.CheckErrors(captureResponse);

            if (!string.IsNullOrEmpty(errorCheck))
            {
                return(PaymentProcessingResult.CreateUnsuccessfulResult(errorCheck));
            }

            var captureResponseDetails = captureResponse.DoCaptureResponseDetails;
            var paymentInfo            = captureResponseDetails.PaymentInfo;

            // Extract the response details.
            payment.ProviderTransactionID = paymentInfo.TransactionID;
            payment.Status = paymentInfo.PaymentStatus.ToString();

            var message = $"[{payment.PaymentMethodName}] [Capture payment-{paymentInfo.TransactionID}] [Status: {paymentInfo.PaymentStatus.ToString()}] " +
                          $".Response: {captureResponse.Ack.ToString()} at Timestamp={captureResponse.Timestamp}: {paymentInfo.GrossAmount.value}{paymentInfo.GrossAmount.currencyID}";

            // add a new order note about this capture
            AddNoteToPurchaseOrder("CAPTURE", message, purchaseOrder.CustomerId, purchaseOrder);

            _orderRepository.Save(purchaseOrder);

            return(PaymentProcessingResult.CreateSuccessfulResult(message));
        }
示例#7
0
        public PayPalPaymentGateway(
            IInventoryProcessor inventoryProcessor,
            IOrderNumberGenerator orderNumberGenerator,
            IOrderRepository orderRepository,
            ITaxCalculator taxCalculator,
            PayPalApiHelper paypalApiHelper)
        {
            _inventoryProcessor   = inventoryProcessor;
            _orderNumberGenerator = orderNumberGenerator;
            _orderRepository      = orderRepository;
            _taxCalculator        = taxCalculator;
            _payPalApiHelper      = paypalApiHelper;

            // ReSharper disable once VirtualMemberCallInConstructor
            _paymentMethodConfiguration = new PayPalConfiguration(Settings);
        }
示例#8
0
        /// <summary>
        /// Process payment when a refund request happens.
        /// </summary>
        /// <remarks>
        /// <para>
        /// See API doc here https://www.x.com/developers/paypal/documentation-tools/api/refundtransaction-api-operation-soap
        /// </para>
        /// <para>
        /// You may offer a refund only for a limited time, usually 60 days. If you need to make a refund after that time, you will need to initiate a new PayPal payment to your buyer.
        /// If you offer the buyer a partial refund, she has 10 days to decline it if she wishes. (Full refunds are automatically processed.)
        /// </para>
        /// </remarks>
        /// <param name="payment">The payment to process.</param>
        /// <param name="orderGroup">The order group to process.</param>
        /// <returns>True if refund was completed, otherwise false and set the message will make the WorkFlow activity raise PaymentExcetion(message).</returns>
        private PaymentProcessingResult ProcessPaymentRefund(IOrderGroup orderGroup, IPayment payment)
        {
            // Implement refund feature logic for current payment gateway
            var refundAmount = payment.Amount;

            if (!(orderGroup is IPurchaseOrder purchaseOrder) || refundAmount <= 0)
            {
                return(PaymentProcessingResult.CreateUnsuccessfulResult(Utilities.Translate("PayPalRefundError")));
            }

            // Call payment gateway API to do refund business
            // Create the Refund Request
            var refundRequest = new RefundTransactionRequestType
            {
                TransactionID = payment.ProviderTransactionID, // original transactionID (which payPal gave us when do ExpressCheckout)
                Memo          = $"[{payment.PaymentMethodName}-{payment.TransactionID}] refunds {refundAmount}{purchaseOrder.Currency} for [PurchaseOrder-{purchaseOrder.OrderNumber}]",
                // NOTE: If RefundType is Full, do not set the amount.
                // refundRequest.RefundType = RefundType.Full; //refund a full or partial amount
                RefundType = RefundType.PARTIAL,                                                //refund a partial amount
                Amount     = _payPalApiHelper.ToPayPalAmount(refundAmount, orderGroup.Currency) // if refund with Partial, we have to set the Amount
            };

            var caller         = PayPalApiHelper.GetPayPalApiCallerServices(_paymentMethodConfiguration);
            var refundResponse = caller.RefundTransaction(new RefundTransactionReq {
                RefundTransactionRequest = refundRequest
            });
            var errorCheck = _payPalApiHelper.CheckErrors(refundResponse);

            if (!string.IsNullOrEmpty(errorCheck))
            {
                return(PaymentProcessingResult.CreateUnsuccessfulResult(errorCheck));
            }

            // Extract the response details.
            payment.TransactionID = refundResponse.RefundTransactionID;

            var message = $"[{payment.PaymentMethodName}] [RefundTransaction-{refundResponse.RefundTransactionID}] " +
                          $"Response: {refundResponse.Ack.ToString()} at Timestamp={refundResponse.Timestamp}: {refundResponse.GrossRefundAmount.value}{refundResponse.GrossRefundAmount.currencyID}";

            // add a new order note about this refund
            AddNoteToPurchaseOrder("REFUND", message, purchaseOrder.CustomerId, purchaseOrder);

            _orderRepository.Save(purchaseOrder);

            return(PaymentProcessingResult.CreateSuccessfulResult(message));
        }
        /// <summary>
        /// The capture success.
        /// </summary>
        /// <param name="invoice">
        /// The invoice.
        /// </param>
        /// <param name="payment">
        /// The payment.
        /// </param>
        /// <param name="amount">
        /// The amount.
        /// </param>
        /// <param name="isPartialPayment">
        /// The is partial payment.
        /// </param>
        /// <returns>
        /// The <see cref="ExpressCheckoutResponse"/>.
        /// </returns>
        public PayPalExpressTransactionRecord Capture(IInvoice invoice, IPayment payment, decimal amount, bool isPartialPayment)
        {
            // Get the transaction record
            var record = payment.GetPayPalTransactionRecord();

            ExpressCheckoutResponse result = null;

            try
            {
                var amountFactory = new PayPalBasicAmountTypeFactory(PayPalApiHelper.GetPayPalCurrencyCode(invoice.CurrencyCode));

                var authorizationId = record.Data.AuthorizationTransactionId;

                // do express checkout
                var request = new DoCaptureRequestType()
                {
                    AuthorizationID = authorizationId,
                    Amount          = amountFactory.Build(amount),
                    CompleteType    = isPartialPayment ? CompleteCodeType.NOTCOMPLETE : CompleteCodeType.COMPLETE
                };

                var doCaptureReq = new DoCaptureReq()
                {
                    DoCaptureRequest = request
                };

                var service           = GetPayPalService();
                var doCaptureResponse = service.DoCapture(doCaptureReq);
                result = _responseFactory.Build(doCaptureResponse, record.Data.Token);

                if (result.Success())
                {
                    var transactionId = doCaptureResponse.DoCaptureResponseDetails.PaymentInfo.TransactionID;
                    record.Data.CaptureTransactionId = transactionId;
                }
            }
            catch (Exception ex)
            {
                result = _responseFactory.Build(ex);
            }

            record.DoCapture = result;
            record.Success   = result.Success();
            return(record);
        }
 /// <summary>
 /// Initializes a new instance of the <see cref="PayPalExpressCheckoutService"/> class.
 /// </summary>
 /// <param name="settings">
 /// The settings.
 /// </param>
 public PayPalExpressCheckoutService(PayPalProviderSettings settings)
     : base(settings)
 {
     _websiteUrl      = PayPalApiHelper.GetBaseWebsiteUrl();
     _responseFactory = new ExpressCheckoutResponseFactory();
 }
示例#11
0
        /// <summary>
        /// Processes the successful transaction, was called when PayPal.com redirect back.
        /// </summary>
        /// <param name="orderGroup">The order group that was processed.</param>
        /// <param name="payment">The order payment.</param>
        /// <param name="acceptUrl">The redirect url when finished.</param>
        /// <param name="cancelUrl">The redirect url when error happens.</param>
        /// <returns>The url redirection after process.</returns>
        public string ProcessSuccessfulTransaction(IOrderGroup orderGroup, IPayment payment, string acceptUrl, string cancelUrl)
        {
            if (HttpContext.Current == null)
            {
                return(cancelUrl);
            }

            if (HttpContext.Current.Session != null)
            {
                HttpContext.Current.Session.Remove("LastCouponCode");
            }

            if (!(orderGroup is ICart cart))
            {
                // return to the shopping cart page immediately and show error messages
                return(ProcessUnsuccessfulTransaction(cancelUrl, Utilities.Translate("CommitTranErrorCartNull")));
            }

            string redirectionUrl;

            using (var scope = new TransactionScope())
            {
                SetSecurityProtocolToTls12();

                var getDetailRequest = new GetExpressCheckoutDetailsRequestType
                {
                    Token = payment.Properties[PayPalExpTokenPropertyName] as string // Add request-specific fields to the request.
                };

                // Execute the API operation and obtain the response.
                var caller             = PayPalApiHelper.GetPayPalApiCallerServices(_paymentMethodConfiguration);
                var getDetailsResponse = caller.GetExpressCheckoutDetails(new GetExpressCheckoutDetailsReq
                {
                    GetExpressCheckoutDetailsRequest = getDetailRequest
                });

                var errorCheck = _payPalApiHelper.CheckErrors(getDetailsResponse);
                if (!string.IsNullOrEmpty(errorCheck))
                {
                    RestoreSecurityProtocol();

                    // unsuccessful get detail call
                    return(ProcessUnsuccessfulTransaction(cancelUrl, errorCheck));
                }

                var expressCheckoutDetailsResponse = getDetailsResponse.GetExpressCheckoutDetailsResponseDetails;
                // get commerceOrderId from what we put to PayPal instead of getting from cookie
                payment.Properties[PayPalOrderNumberPropertyName] = expressCheckoutDetailsResponse.InvoiceID;

                //process details sent from paypal, changing addresses if required
                string emptyAddressMsg;

                //process billing address
                var payPalBillingAddress = expressCheckoutDetailsResponse.BillingAddress;
                if (payPalBillingAddress != null && AddressHandling.IsAddressChanged(payment.BillingAddress, payPalBillingAddress))
                {
                    emptyAddressMsg = _payPalApiHelper.ProcessOrderAddress(expressCheckoutDetailsResponse.PayerInfo, payPalBillingAddress, payment.BillingAddress, CustomerAddressTypeEnum.Billing, "CommitTranErrorPayPalBillingAddressEmpty");
                    if (!string.IsNullOrEmpty(emptyAddressMsg))
                    {
                        RestoreSecurityProtocol();

                        return(ProcessUnsuccessfulTransaction(cancelUrl, emptyAddressMsg));
                    }
                }

                //process shipping address
                var payPalShippingAddress = expressCheckoutDetailsResponse.PaymentDetails[0].ShipToAddress;
                if (payPalShippingAddress != null && AddressHandling.IsAddressChanged(cart.GetFirstShipment().ShippingAddress, payPalShippingAddress))
                {
                    //when address was changed on PayPal site, it might cause changing tax value changed and changing order value also.
                    var taxValueBefore = _taxCalculator.GetTaxTotal(cart, cart.Market, cart.Currency);

                    var shippingAddress = orderGroup.CreateOrderAddress("address");

                    emptyAddressMsg = _payPalApiHelper.ProcessOrderAddress(expressCheckoutDetailsResponse.PayerInfo, payPalShippingAddress, shippingAddress, CustomerAddressTypeEnum.Shipping, "CommitTranErrorPayPalShippingAddressEmpty");
                    if (!string.IsNullOrEmpty(emptyAddressMsg))
                    {
                        RestoreSecurityProtocol();

                        return(ProcessUnsuccessfulTransaction(cancelUrl, emptyAddressMsg));
                    }

                    cart.GetFirstShipment().ShippingAddress = shippingAddress;

                    var taxValueAfter = _taxCalculator.GetTaxTotal(cart, cart.Market, cart.Currency);
                    if (taxValueBefore != taxValueAfter)
                    {
                        RestoreSecurityProtocol();

                        _orderRepository.Save(cart); // Saving cart to submit order address changed.
                        scope.Complete();
                        return(ProcessUnsuccessfulTransaction(cancelUrl, Utilities.Translate("ProcessPaymentTaxValueChangedWarning")));
                    }
                }

                // Add request-specific fields to the request.
                // Create the request details object.
                var doExpressChkOutPaymentReqDetails = CreateExpressCheckoutPaymentRequest(getDetailsResponse, orderGroup, payment);

                // Execute the API operation and obtain the response.
                var doCheckOutResponse = caller.DoExpressCheckoutPayment(new DoExpressCheckoutPaymentReq
                {
                    DoExpressCheckoutPaymentRequest = new DoExpressCheckoutPaymentRequestType(doExpressChkOutPaymentReqDetails)
                });

                errorCheck = _payPalApiHelper.CheckErrors(doCheckOutResponse);
                if (!string.IsNullOrEmpty(errorCheck))
                {
                    RestoreSecurityProtocol();

                    // unsuccessful doCheckout response
                    return(ProcessUnsuccessfulTransaction(cancelUrl, errorCheck));
                }

                // everything is fine, this is a flag to tell ProcessPayment know about this case: redirect back from PayPal with accepted payment
                var errorMessages = new List <string>();
                var cartCompleted = DoCompletingCart(cart, errorMessages);

                if (!cartCompleted)
                {
                    RestoreSecurityProtocol();

                    return(UriSupport.AddQueryString(cancelUrl, "message", string.Join(";", errorMessages.Distinct().ToArray())));
                }

                // Place order
                var purchaseOrder = MakePurchaseOrder(doCheckOutResponse, cart, payment);

                // Commit changes
                scope.Complete();

                redirectionUrl = CreateRedirectionUrl(purchaseOrder, acceptUrl, payment.BillingAddress.Email);

                RestoreSecurityProtocol();
            }

            _logger.Information($"PayPal transaction succeeds, redirect end user to {redirectionUrl}");
            return(redirectionUrl);
        }