/// <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(PaymentTransactionFailedMessage));
            }

            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));
        }
        /// <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;
            var purchaseOrder = orderGroup as IPurchaseOrder;

            if (purchaseOrder == null || 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))
            {
                _logger.Error(errorCheck);
                return(PaymentProcessingResult.CreateUnsuccessfulResult(PaymentTransactionFailedMessage));
            }

            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.ToString()}: {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));
        }
        /// <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;
            var purchaseOrder = (orderGroup as IPurchaseOrder);

            if (purchaseOrder == null || 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))
            {
                _logger.Error(errorCheck);
                return(PaymentProcessingResult.CreateUnsuccessfulResult(PaymentTransactionFailedMessage));
            }

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

            var message = $"[{payment.PaymentMethodName}] [RefundTransaction-{refundResponse.RefundTransactionID}] " +
                          $"Response: {refundResponse.Ack.ToString()} at Timestamp={refundResponse.Timestamp.ToString()}: {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));
        }
        public PayPalPaymentGateway(
            IFeatureSwitch featureSwitch,
            IInventoryProcessor inventoryProcessor,
            IOrderNumberGenerator orderNumberGenerator,
            IOrderRepository orderRepository,
            IOrderGroupCalculator orderGroupCalculator,
            PayPalAPIHelper paypalAPIHelper)
        {
            _featureSwitch        = featureSwitch;
            _inventoryProcessor   = inventoryProcessor;
            _orderNumberGenerator = orderNumberGenerator;
            _orderRepository      = orderRepository;
            _orderGroupCalculator = orderGroupCalculator;
            _payPalAPIHelper      = paypalAPIHelper;

            _paymentMethodConfiguration = new PayPalConfiguration(Settings);
        }
示例#5
0
        /// <summary>
        /// Handles the Load event of the Page control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void Page_Load(object sender, System.EventArgs e)
        {
            if (_payPalConfiguration == null)
            {
                _payPalConfiguration = new PayPalConfiguration();
            }

            var pal = _payPalConfiguration.PAL;

            if (string.IsNullOrEmpty(pal))
            {
                pal = ViewState["PAL"] as string;
            }

            if (string.IsNullOrEmpty(pal))
            {
                //Obtain the PAL code using API
                var caller = PayPalAPIHelper.GetPayPalAPICallerServices(_payPalConfiguration);
                if (caller != null)
                {
                    var palResponse = caller.GetPalDetails(new GetPalDetailsReq()
                    {
                        GetPalDetailsRequest = new GetPalDetailsRequestType()
                    });
                    if (palResponse.Ack == AckCodeType.SUCCESSWITHWARNING || palResponse.Ack == AckCodeType.SUCCESS)
                    {
                        pal = palResponse.Pal;
                        ViewState["PAL"] = pal;
                    }
                }
            }

            SetupImageMark(_payPalConfiguration, pal);

            Message.Text = Utilities.Translate("PayPalText");

            if (string.Equals(Request["accept"], "false") && !string.IsNullOrEmpty(Request.QueryString["hash"]))
            {
                ErrorManager.GenerateError(Utilities.Translate("CancelMessage"));
                return;
            }
        }
        /// <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);
            }

            var cart = orderGroup as ICart;

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

            string redirectionUrl;
            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))
            {
                // unsuccessful get detail call
                _logger.Error(errorCheck);
                return(ProcessUnsuccessfulTransaction(cancelUrl, PaymentTransactionFailedMessage));
            }

            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
            var emptyAddressMsg = string.Empty;

            //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))
                {
                    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 = cart.GetTaxTotal(_orderGroupCalculator);

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

                emptyAddressMsg = _payPalAPIHelper.ProcessOrderAddress(expressCheckoutDetailsResponse.PayerInfo, payPalShippingAddress, shippingAddress, CustomerAddressTypeEnum.Shipping, "CommitTranErrorPayPalShippingAddressEmpty");
                if (!string.IsNullOrEmpty(emptyAddressMsg))
                {
                    return(ProcessUnsuccessfulTransaction(cancelUrl, emptyAddressMsg));
                }

                cart.GetFirstShipment().ShippingAddress = shippingAddress;

                var taxValueAfter = cart.GetTaxTotal(_orderGroupCalculator);
                if (taxValueBefore != taxValueAfter)
                {
                    _orderRepository.Save(cart); // Saving cart to submit order address changed.
                    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))
            {
                // unsuccessful doCheckout response
                _logger.Error(errorCheck);
                return(ProcessUnsuccessfulTransaction(cancelUrl, PaymentTransactionFailedMessage));
            }

            // 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)
            {
                return(UriUtil.AddQueryString(cancelUrl, "message", string.Join(";", errorMessages.Distinct().ToArray())));
            }

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

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

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