public ExpressCheckoutResult SetExpressCheckout() { HttpContext context = HttpContext.Current; User user = Token.Instance.User; Basket basket = user.Basket; //MAKE SURE BASKET IS PROPERLY PACKAGED FOR CHECKOUT basket.Package(); //GET EXISTING SESSION IF IT IS PRESENT ExpressCheckoutSession existingSession = ExpressCheckoutSession.Current; if (existingSession != null) { WebTrace.Write("Existing session token: " + existingSession.Token); } //CREATE THE EXPRESS CHECKOUT REQUEST OBJECT SetExpressCheckoutRequestType expressCheckoutRequest = new SetExpressCheckoutRequestType(); expressCheckoutRequest.SetExpressCheckoutRequestDetails = new SetExpressCheckoutRequestDetailsType(); if (existingSession != null) { expressCheckoutRequest.SetExpressCheckoutRequestDetails.Token = existingSession.Token; } expressCheckoutRequest.Version = "1.0"; //GET THE CURRENCY FOR THE TRANSACTION string baseCurrencyCode = Token.Instance.Store.BaseCurrency.ISOCode; CurrencyCodeType baseCurrency = PayPalProvider.GetPayPalCurrencyType(baseCurrencyCode); //BUILD THE REQUEST DETAILS SetExpressCheckoutRequestDetailsType expressCheckoutDetails = expressCheckoutRequest.SetExpressCheckoutRequestDetails; LSDecimal basketTotal = basket.Items.TotalPrice(); WebTrace.Write("Basket Total: " + basketTotal.ToString()); expressCheckoutDetails.OrderTotal = new BasicAmountType(); expressCheckoutDetails.OrderTotal.currencyID = baseCurrency; expressCheckoutDetails.OrderTotal.Value = string.Format("{0:##,##0.00}", basketTotal); expressCheckoutDetails.MaxAmount = new BasicAmountType(); expressCheckoutDetails.MaxAmount.currencyID = baseCurrency; expressCheckoutDetails.MaxAmount.Value = string.Format("{0:##,##0.00}", basketTotal + 50); //SET THE URLS string storeUrl = GetStoreUrl(); expressCheckoutDetails.ReturnURL = storeUrl + "/PayPalExpressCheckout.aspx?Action=GET"; expressCheckoutDetails.CancelURL = storeUrl + "/PayPalExpressCheckout.aspx?Action=CANCEL"; //SET THE CUSTOM VALUE TO THE USER ID FOR MATCHING DURING GET expressCheckoutDetails.Custom = "UID" + basket.UserId.ToString(); //SET THE CUSTOMER ADDRESS Address billingAddress = user.PrimaryAddress; AddressType address = new AddressType(); address.Name = billingAddress.FirstName + " " + billingAddress.LastName; address.Street1 = billingAddress.Address1; address.Street2 = billingAddress.Address2; address.CityName = billingAddress.City; address.PostalCode = billingAddress.PostalCode; if (billingAddress.Country != null) { address.Country = PayPalProvider.GetPayPalCountry(billingAddress.CountryCode); } else { address.Country = CountryCodeType.US; } address.CountrySpecified = true; expressCheckoutDetails.BuyerEmail = billingAddress.Email; expressCheckoutDetails.Address = address; //SET THE PAYMENT ACTION expressCheckoutDetails.PaymentAction = this.UseAuthCapture ? PaymentActionCodeType.Sale : PaymentActionCodeType.Authorization; expressCheckoutDetails.PaymentActionSpecified = true; //EXECUTE REQUEST SetExpressCheckoutResponseType expressCheckoutResponse; context.Trace.Write("DO SOAP CALL"); expressCheckoutResponse = (SetExpressCheckoutResponseType)SoapCall("SetExpressCheckout", expressCheckoutRequest); context.Trace.Write("CHECK SOAP RESULT"); if (expressCheckoutResponse == null) { ErrorType[] customErrorList = new ErrorType[1]; ErrorType customError = new ErrorType(); customError.ErrorCode = "NORESP"; customError.ShortMessage = "No Response From Server"; customError.LongMessage = "The PayPal service is unavailable at this time."; customErrorList[0] = customError; return(new ExpressCheckoutResult(0, string.Empty, customErrorList)); } //IF ERRORS ARE IN RESPONSE, RETURN THEM AND EXIT PROCESS if (expressCheckoutResponse.Errors != null) { return(new ExpressCheckoutResult(0, string.Empty, expressCheckoutResponse.Errors)); } //NO ERRORS FOUND, PUT PAYPAL DETAILS INTO SESSION context.Trace.Write("Store PayPal Token In Session"); ExpressCheckoutSession newSession = new ExpressCheckoutSession(); newSession.Token = expressCheckoutResponse.Token; newSession.TokenExpiration = DateTime.UtcNow.AddHours(3); newSession.Save(); context.Trace.Write("Saved PayPal Token:" + newSession.Token); context.Trace.Write("Token Expiration:" + newSession.TokenExpiration.ToLongDateString()); //RETURN TO CALLER INCLUDING REDIRECTION URL string redirectUrl = "https://www" + (this.UseSandbox ? ".sandbox" : string.Empty) + ".paypal.com/webscr?cmd=_express-checkout&token=" + expressCheckoutResponse.Token; return(new ExpressCheckoutResult(0, redirectUrl, null)); }
public void ProcessRequest(HttpContext context) { //GET REFERENCE TO REQUEST HttpRequest request = context.Request; HttpResponse response = context.Response; TraceContext trace = context.Trace; //RECORD FORM VALUES TO TRACE OUTPUT foreach (string key in request.Form) { context.Trace.Write(key + ":" + request.Form[key]); } //SETUP DEFAULT REDIRECT URL string redirectUrl = "~/Default.aspx"; //INITIALIZE THE PAYPAL PROVIDER PaymentGateway paypalGateway = PayPalProvider.GetPayPalPaymentGateway(true); if (paypalGateway == null) { response.Redirect(redirectUrl); } //LOOK FOR ORDER ID int orderId; string customValue = request.Form["custom"]; if (!String.IsNullOrEmpty(customValue)) { int index = customValue.IndexOf(":"); if (index > 0) { orderId = AlwaysConvert.ToInt(customValue.Substring(0, index)); } else { orderId = AlwaysConvert.ToInt(customValue); } } else { // TRY TO LOCATE ORDER ID AS QUERY STRING PARAMETER orderId = AlwaysConvert.ToInt(request.QueryString["OrderId"]); } //IF ORDER ID WAS NOT IN CUSTOM, SEE IF WE CAN FIND THE ORDER VIA TRANSACTION ID if (orderId == 0) { trace.Write("OrderId not found in custom field; lookup via transaction ID"); string parentTransactionId = IpnProcessor.GetFormValue(request.Form, "parent_txn_id"); if (!string.IsNullOrEmpty(parentTransactionId) && (paypalGateway != null)) { trace.Write("Query for parent transaction " + parentTransactionId); orderId = FindPayPalOrderId(paypalGateway.PaymentGatewayId, parentTransactionId); if (orderId != 0) { trace.Write("Order ID Found: " + orderId.ToString()); } } } //TRY TO LOAD ORDER Order order = OrderDataSource.Load(orderId); //IF ORDER LOAD FAILS, STOP PROCESSING AND REDIRECT if (order == null) { response.Redirect(redirectUrl); } //ORDER LOAD SUCCESSFUL, UPDATE DEFAULT REDIRECT URL redirectUrl = "~/Members/MyOrder.aspx?OrderId=" + orderId.ToString(); //IF GATEWAY NOT FOUND, STOP PROCESSING AND REDIRECT PayPalProvider provider = (PayPalProvider)paypalGateway.GetInstance(); //GET TRANSACTION AMOUNT LSDecimal curSignedPayment = AlwaysConvert.ToDecimal(request.Form["mc_gross"]); LSDecimal curPayment = Math.Abs((Decimal)curSignedPayment); context.Trace.Write("Transaction Amount is " + curPayment.ToString()); if (curPayment != 0) { //VERIFY PAYMENT NOTIFICATION WITH PAYPAL bool valid = provider.ValidateNotification(request.Form.ToString()); if (!valid) { response.Redirect(redirectUrl); } //VERIFY THE RECEIVER EMAIL string lowerReceiverEmail = AlwaysConvert.ToString(request.Form["receiver_email"]).ToLowerInvariant(); string lowerProviderAccount = provider.PayPalAccount.ToLowerInvariant(); if (lowerReceiverEmail != lowerProviderAccount) { context.Trace.Write("Receiver Email (" + lowerReceiverEmail + ") does not match Primary Account (" + lowerProviderAccount + ")"); response.Redirect(redirectUrl); } //CHECK WHETHER TRANSACTION IS ALREADY PRESENT IN DATABASE string paypalTransactionId = IpnProcessor.GetFormValue(request.Form, "txn_id"); string authTransactionId = IpnProcessor.GetFormValue(request.Form, "auth_id"); string paymentStatus = IpnProcessor.GetFormValue(request.Form, "payment_status").ToUpperInvariant(); string authStatus = IpnProcessor.GetFormValue(request.Form, "auth_status").ToUpperInvariant(); context.Trace.Write("Transaction ID Is " + paypalTransactionId); context.Trace.Write("Payment Status Is " + paymentStatus); context.Trace.Write("Auth Status Is " + authStatus); //CHECK FOR THIS PAYPAL TRANSACTION (MATCHING PROVIDER, PAYPAL TRANSACTION ID, AND PAYMENT STATUS) Payment payment = null; Transaction pendingTransaction = null; if (!string.IsNullOrEmpty(paypalTransactionId)) { TransactionCollection matchingTransactions = TransactionDataSource.LoadForProviderTransaction(paypalGateway.PaymentGatewayId, paypalTransactionId); foreach (Transaction tx in matchingTransactions) { //WHEN PAYMENT IS BY ECHECK, IPN ISSUES A PENDING TRANSACTION //SECOND IPN COMES FOR COMPLETED STATUS USING SAME TRANSACTION ID if ((tx.ResponseCode == "PENDING") && (paymentStatus != "PENDING")) { //WE HAVE TO GET THE TRANSACTION VIA THE PAYMENT OBJECT //OTHERWISE WE WILL HAVE PROBLEMS WITH DATA CONSISTENCY LATER payment = tx.Payment; foreach (Transaction ptx in payment.Transactions) { if (ptx.TransactionId == tx.TransactionId) { pendingTransaction = ptx; } } } else if ((tx.TransactionType != TransactionType.Void) && (paymentStatus == "VOIDED")) { //IF WE VOID AN AUTHORIZATION, THE VOID HAS THE SAME TXID //THE AUTHORIZATION WILL HAVE A BLANK RESPONSE CODE //THE VOID SHOULD HAVE A 'VOIDED' RESPONSE CODE //(THIS TRANSACTION IS NOT A MATCH AND SHOULD BE IGNORED) } else { //NO FURTHER PROCESSING, REDIR TO ORDER SCREEN context.Trace.Write("Transaction ID " + paypalTransactionId + " Already Exists in Database"); response.Redirect(redirectUrl); } } } Transaction transaction = null; PaymentMethod paypalMethod = PayPalProvider.GetPayPalPaymentMethod(true); PaymentCollection orderPayments = order.Payments; Transaction authTransaction = null; PaymentStatus[] validAuthStatuses = { PaymentStatus.Unprocessed, PaymentStatus.AuthorizationPending }; context.Trace.Write("Processing Payment Status: " + paymentStatus); switch (paymentStatus) { case "PENDING": //THIS IS A PENDING TRANSACTION, GET PENDING REASON AND FIND OUT IF IT IS AN ECHECK WAITING TO CLEAR string pendingReason = IpnProcessor.GetFormValue(request.Form, "pending_reason").ToLowerInvariant(); bool isPendingeCheck = (pendingReason == "echeck"); bool isPendingAuthorization = (pendingReason == "authorization"); context.Trace.Write("Pending Reason: " + pendingReason); context.Trace.Write("Is Pending eCheck: " + isPendingeCheck.ToString()); context.Trace.Write("Is Pending Authorization: " + isPendingAuthorization.ToString()); //FIND THE PAYPAL PAYMENT THAT IS UNPROCESSED OR PENDING AUTHORIZATION payment = FindPaypalPayment(paypalMethod.PaymentMethodId, orderPayments, validAuthStatuses); if (payment != null) { //SEE IF WE CAN FIND A PENDING PAYPAL TRANSACTION WITHOUT A TXID foreach (Transaction tx in payment.Transactions) { if ((tx.ResponseCode == "PENDING") && string.IsNullOrEmpty(tx.ProviderTransactionId)) { transaction = tx; } } } //IF WE DID NOT FIND AN EXISTING TRANSACTION, CREATE A NEW ONE if (transaction == null) { transaction = new Transaction(); } //UPDATE THE TRANSACTION VALUES transaction.TransactionType = (isPendingeCheck ? TransactionType.Capture : TransactionType.Authorize); transaction.PaymentGatewayId = paypalGateway.PaymentGatewayId; transaction.ProviderTransactionId = IpnProcessor.GetFormValue(request.Form, "txn_id"); transaction.TransactionDate = AlwaysConvert.ToDateTime(request.Form["payment_date"], DateTime.UtcNow).ToUniversalTime(); transaction.Amount = AlwaysConvert.ToDecimal(IpnProcessor.GetFormValue(request.Form, "mc_gross")); transaction.TransactionStatus = TransactionStatus.Successful; if (isPendingAuthorization) { //THIS IS AN EXPECTED RESPONSE, NO NEED TO SAVE THE REASON CODES transaction.ResponseMessage = string.Empty; transaction.ResponseCode = string.Empty; } else { transaction.ResponseMessage = pendingReason; transaction.ResponseCode = "PENDING"; } transaction.AuthorizationCode = IpnProcessor.GetFormValue(request.Form, "auth_id"); transaction.RemoteIP = IpnProcessor.GetFormValue(request.ServerVariables, "REMOTE_ADDR"); transaction.Referrer = IpnProcessor.GetFormValue(request.ServerVariables, "HTTP_REFERER"); //CREATE A PAYMENT IF AN EXISTING ONE WAS NOT FOUND if (payment == null) { payment = new Payment(); payment.OrderId = orderId; payment.PaymentMethodId = paypalMethod.PaymentMethodId; payment.PaymentMethodName = paypalMethod.Name; order.Payments.Add(payment); } //UPDATE PAYMENT DETAILS payment.ReferenceNumber = IpnProcessor.GetFormValue(request.Form, "payer_email"); payment.Amount = transaction.Amount; payment.PaymentDate = transaction.TransactionDate; if (isPendingAuthorization) { payment.PaymentStatus = PaymentStatus.Authorized; payment.PaymentStatusReason = string.Empty; } else { payment.PaymentStatus = (isPendingeCheck ? PaymentStatus.CapturePending : PaymentStatus.AuthorizationPending); payment.PaymentStatusReason = transaction.ResponseMessage; } //ADD IN TRANSACTION payment.Transactions.Add(transaction); break; case "COMPLETED": //IF THIS IS A CAPTURE FROM AN AUTHORIZATION, FIND THE AUTHORIZATION TRANSACTION //AND UPDATE THE STATUS ACCORDINGLY, DEPENDING ON WHETHER ADDITIONAL SETTLEMENT TRANSACTIONS REMAIN (INTREMSETTLE > 0) authTransaction = null; authTransactionId = IpnProcessor.GetFormValue(request.Form, "auth_id"); if (!string.IsNullOrEmpty(authTransactionId)) { TransactionCollection matchingTransactions = TransactionDataSource.LoadForProviderTransaction(paypalGateway.PaymentGatewayId, authTransactionId); //SHOULD ONLY BE ONE if (matchingTransactions.Count > 0) { authTransaction = matchingTransactions[0]; } } //IF PAYPAL IS RUNNING IN CAPTURE MODE, WE MAY HAVE A COMPLETED PAYMENT //WITH A PENDING OR UNPROCESSED PAYMENT ALREADY ASSOCIATED TO ORDER if (pendingTransaction == null) { //FIND THE PAYPAL PAYMENT THAT IS UNPROCESSED OR PENDING payment = FindPaypalPayment(paypalMethod.PaymentMethodId, orderPayments, validAuthStatuses); if (payment != null) { //SEE IF WE CAN FIND A PENDING PAYPAL TRANSACTION WITHOUT A TXID foreach (Transaction tx in payment.Transactions) { if ((tx.ResponseCode == "PENDING") && string.IsNullOrEmpty(tx.ProviderTransactionId)) { pendingTransaction = tx; } } } } //SEE IF THIS TRANSACTION WAS PENDING (SUCH AS A CHECK WAITING TO CLEAR) if (pendingTransaction != null) { //GET THE PENDING TRANSACTION AND PAYMENT payment = order.Payments[order.Payments.IndexOf(pendingTransaction.PaymentId)]; transaction = payment.Transactions[payment.Transactions.IndexOf(pendingTransaction.TransactionId)]; } else { //THIS IS NOT A PENDING TRANSACTION //LOCATE THE APPROPRIATE PAYMENT if (authTransaction != null) { payment = order.Payments[order.Payments.IndexOf(authTransaction.PaymentId)]; } else { //FIND THE PAYPAL PAYMENT THAT CAN BE CAPTURED PaymentStatus[] validCaptureStatuses = { PaymentStatus.Unprocessed, PaymentStatus.AuthorizationPending, PaymentStatus.Authorized, PaymentStatus.CaptureFailed, PaymentStatus.CapturePending }; payment = FindPaypalPayment(paypalMethod.PaymentMethodId, orderPayments, validCaptureStatuses); //CREATE A PAYMENT IF AN EXISTING ONE WAS NOT FOUND if (payment == null) { payment = new Payment(); payment.OrderId = orderId; payment.PaymentMethodId = paypalMethod.PaymentMethodId; payment.PaymentMethodName = paypalMethod.Name; order.Payments.Add(payment); } } //CREATE A NEW TRANSACTION RECORD transaction = new Transaction(); transaction.PaymentId = payment.PaymentId; } //UPDATE THE TRANSACTION DETAILS transaction.TransactionType = TransactionType.Capture; transaction.PaymentGatewayId = paypalGateway.PaymentGatewayId; transaction.TransactionDate = AlwaysConvert.ToDateTime(request.Form["payment_date"], DateTime.UtcNow).ToUniversalTime(); transaction.RemoteIP = IpnProcessor.GetFormValue(request.ServerVariables, "REMOTE_ADDR"); transaction.Referrer = IpnProcessor.GetFormValue(request.ServerVariables, "HTTP_REFERER"); transaction.TransactionStatus = TransactionStatus.Successful; transaction.ProviderTransactionId = IpnProcessor.GetFormValue(request.Form, "txn_id"); transaction.AuthorizationCode = IpnProcessor.GetFormValue(request.Form, "auth_id"); transaction.Amount = AlwaysConvert.ToDecimal(IpnProcessor.GetFormValue(request.Form, "mc_gross")); transaction.ResponseCode = paymentStatus; transaction.ResponseMessage = string.Empty; //HANDLE PARTIAL / FINAL CAPTURES int remainingSettle = AlwaysConvert.ToInt(IpnProcessor.GetFormValue(request.Form, "remaining_settle")); if (remainingSettle == 0) { //THIS IS A FINAL CAPTURE transaction.TransactionType = TransactionType.Capture; //SET PAYMENT AMOUNT TO SUM OF ALL CAPTURES LSDecimal totalCaptures = 0; foreach (Transaction tx in payment.Transactions) { if ((transaction.TransactionId != tx.TransactionId) && (tx.TransactionType == TransactionType.PartialCapture || tx.TransactionType == TransactionType.Capture)) { totalCaptures += tx.Amount; } } totalCaptures += transaction.Amount; payment.Amount = totalCaptures; } else { //THIS IS A PARTIAL CAPTURE transaction.TransactionType = TransactionType.PartialCapture; //LEAVE PAYMENT AMOUNT ALONE (AMOUNT OF AUTHORIZATION) } //UPDATE PAYMENT DETAILS payment.PaymentDate = transaction.TransactionDate; payment.PaymentStatus = (remainingSettle == 0) ? PaymentStatus.Captured : PaymentStatus.Authorized; payment.PaymentStatusReason = string.Empty; //ADD IN TRANSACTION IF NEEDED if (transaction.TransactionId == 0) { payment.Transactions.Add(transaction); } break; case "REFUNDED": case "REVERSED": //GET THE REFUND AMOUNT LSDecimal refundAmount = Math.Abs(AlwaysConvert.ToDecimal(IpnProcessor.GetFormValue(request.Form, "mc_gross"))); //TRY TO LOCATE THE CORRECT PAYMENT BASED ON CAPTURE TRANSACITON ID payment = FindPayPalPayment(paypalGateway.PaymentGatewayId, orderPayments, IpnProcessor.GetFormValue(request.Form, "parent_txn_id")); if (payment == null) { //SEE IF WE CAN FIND THE PAYMENT VIA AUTH TRANSACTION ID payment = FindPayPalPayment(paypalGateway.PaymentGatewayId, orderPayments, IpnProcessor.GetFormValue(request.Form, "auth_id")); } //CREATE A REFUND TRANSACTION transaction = new Transaction(); //CREATE A PAYMENT IF AN EXISTING ONE WAS NOT FOUND if (payment == null) { payment = new Payment(); payment.OrderId = orderId; payment.PaymentMethodId = paypalMethod.PaymentMethodId; payment.PaymentMethodName = paypalMethod.Name; payment.Amount = -1 * refundAmount; transaction.TransactionType = TransactionType.Refund; order.Payments.Add(payment); } else { if (payment.Amount == refundAmount) { //FULL REFUND transaction.TransactionType = TransactionType.Refund; payment.PaymentStatus = PaymentStatus.Refunded; } else { //PARTIAL REFUND transaction.TransactionType = TransactionType.PartialRefund; payment.Amount -= refundAmount; payment.PaymentStatus = PaymentStatus.Captured; } } transaction.PaymentGatewayId = paypalGateway.PaymentGatewayId; transaction.ProviderTransactionId = IpnProcessor.GetFormValue(request.Form, "txn_id"); transaction.TransactionDate = AlwaysConvert.ToDateTime(request.Form["payment_date"], DateTime.UtcNow).ToUniversalTime(); transaction.TransactionStatus = TransactionStatus.Successful; transaction.AuthorizationCode = IpnProcessor.GetFormValue(request.Form, "auth_id"); transaction.RemoteIP = IpnProcessor.GetFormValue(request.ServerVariables, "REMOTE_ADDR"); transaction.Referrer = IpnProcessor.GetFormValue(request.ServerVariables, "HTTP_REFERER"); transaction.Amount = refundAmount; string responseMessage = IpnProcessor.GetFormValue(request.Form, "reason_code"); if (responseMessage != "refund") { transaction.ResponseCode = paymentStatus; transaction.ResponseMessage = responseMessage; } //UPDATE PAYMENT DETAILS payment.PaymentDate = transaction.TransactionDate; payment.PaymentStatusReason = string.Empty; //ADD IN TRANSACTION payment.Transactions.Add(transaction); break; case "VOIDED": //SEE IF WE CAN FIND THE PAYMENT VIA AUTH TRANSACTION ID payment = FindPayPalPayment(paypalGateway.PaymentGatewayId, orderPayments, IpnProcessor.GetFormValue(request.Form, "auth_id")); //WE ONLY NEED TO CONTINUE IF A PAYMENT TO VOID WAS FOUND if (payment != null) { //PAYPAL DOES NOT SEND THE AMOUNT OF THE VOID //SO IF THIS PAYMENT WAS PARTIALLY CAPTURED, WE NEED TO KNOW HOW MUCH TO VOID LSDecimal remainingAuthorization = payment.Transactions.GetRemainingAuthorized(); if (remainingAuthorization > 0) { //CREATE A VOID TRANSACTION transaction = new Transaction(); transaction.TransactionType = TransactionType.Void; transaction.Amount = remainingAuthorization; transaction.PaymentGatewayId = paypalGateway.PaymentGatewayId; transaction.ProviderTransactionId = IpnProcessor.GetFormValue(request.Form, "txn_id"); transaction.TransactionDate = AlwaysConvert.ToDateTime(request.Form["payment_date"], DateTime.UtcNow).ToUniversalTime(); transaction.TransactionStatus = TransactionStatus.Successful; transaction.AuthorizationCode = IpnProcessor.GetFormValue(request.Form, "auth_id"); transaction.RemoteIP = IpnProcessor.GetFormValue(request.ServerVariables, "REMOTE_ADDR"); transaction.Referrer = IpnProcessor.GetFormValue(request.ServerVariables, "HTTP_REFERER"); //UPDATE PAYMENT DETAILS payment.PaymentDate = transaction.TransactionDate; payment.PaymentStatusReason = string.Empty; if (payment.Amount == remainingAuthorization) { //FULL VOID, CHANGE PAYMENT STATUS TO VOID payment.PaymentStatus = PaymentStatus.Void; } else { //PARTIAL VOID, REDUCE PAYMENT AMOUNT BY VOID payment.Amount -= remainingAuthorization; //PAYMENT HAS NO REMAINING AUTHORIZATION AND SO IT IS CAPTURED payment.PaymentStatus = PaymentStatus.Captured; } //ADD IN TRANSACTION payment.Transactions.Add(transaction); } } break; case "FAILED": //THIS IS A FAILED E-CHECK //PENDINGTRANSACTION SHOULD HAVE BEEN OBTAINED ABOVE if (payment != null && pendingTransaction != null) { pendingTransaction.TransactionStatus = TransactionStatus.Failed; //MAKE SURE TO CLEAR OUT PENDING RESPONSECODE pendingTransaction.ResponseCode = string.Empty; //GET THE CURRENT TRANSACTION DATE pendingTransaction.TransactionDate = AlwaysConvert.ToDateTime(request.Form["payment_date"], DateTime.UtcNow).ToUniversalTime(); //UPDATE PAYMENT DETAILS payment.PaymentDate = pendingTransaction.TransactionDate; payment.PaymentStatus = (IsVoidableFailure(payment) ? PaymentStatus.Void : PaymentStatus.CaptureFailed); payment.PaymentStatusReason = string.Empty; //SAVE PAYMENT (AND CHILD TRANSACTIONS) payment.Save(); } break; default: Logger.Warn("PayPal IPN transaction " + paypalTransactionId + " with a \"" + paymentStatus + "\" status was unhandled."); break; } //IF PAYMENT IS SET, SAVE UPDATES if (payment != null) { payment.Save(); } } response.Redirect(redirectUrl); }
public ExpressCheckoutResult DoExpressCheckout() { HttpContext context = HttpContext.Current; TraceContext trace = context.Trace; string traceCategory = this.GetType().ToString(); ExpressCheckoutSession paypalSession = ExpressCheckoutSession.Current; if (paypalSession == null) { //EXIT WITH EXCEPTION ErrorType[] customErrorList = new ErrorType[1]; ErrorType customError = new ErrorType(); customError.ErrorCode = "SESSION"; customError.ShortMessage = "Missing Token"; customError.LongMessage = "The PayPal session token was expired or unavailable. Please try again."; customErrorList[0] = customError; return(new ExpressCheckoutResult(0, string.Empty, customErrorList)); } trace.Write(traceCategory, "Detected PayPal Token:" + paypalSession.Token); trace.Write(traceCategory, "Token Expiration:" + paypalSession.TokenExpiration.ToLongDateString()); if (string.IsNullOrEmpty(paypalSession.PayerID)) { //EXIT WITH EXCEPTION ErrorType[] customErrorList = new ErrorType[1]; ErrorType customError = new ErrorType(); customError.ErrorCode = "SESSION"; customError.ShortMessage = "Missing Payer ID"; customError.LongMessage = "The PayPal Payer ID is not present."; customErrorList[0] = customError; return(new ExpressCheckoutResult(0, string.Empty, customErrorList)); } trace.Write(traceCategory, "Detected PayPal Payer ID:" + paypalSession.PayerID); //GET THE CURRENCY FOR THE TRANSACTION string storeCurrencyCode = Token.Instance.Store.BaseCurrency.ISOCode; CurrencyCodeType baseCurrencyCode = PayPalProvider.GetPayPalCurrencyType(storeCurrencyCode); //CREATE THE EXPRESS CHECKOUT DoExpressCheckoutPaymentRequestType expressCheckoutRequest = new DoExpressCheckoutPaymentRequestType(); expressCheckoutRequest.DoExpressCheckoutPaymentRequestDetails = new DoExpressCheckoutPaymentRequestDetailsType(); expressCheckoutRequest.DoExpressCheckoutPaymentRequestDetails.Token = paypalSession.Token; expressCheckoutRequest.DoExpressCheckoutPaymentRequestDetails.PaymentAction = this.UseAuthCapture ? PaymentActionCodeType.Sale : PaymentActionCodeType.Authorization; expressCheckoutRequest.DoExpressCheckoutPaymentRequestDetails.PaymentActionSpecified = true; expressCheckoutRequest.DoExpressCheckoutPaymentRequestDetails.PayerID = paypalSession.PayerID; expressCheckoutRequest.Version = "1.0"; //SET THE ORDER TOTAL AMOUNTS Basket basket = Token.Instance.User.Basket; trace.Write(traceCategory, "Set Order Totals"); LSDecimal curOrderTotal = basket.Items.TotalPrice(); LSDecimal curShippingTotal = basket.Items.TotalPrice(OrderItemType.Shipping) + GetShippingCouponTotal(basket.Items); LSDecimal curHandlingTotal = basket.Items.TotalPrice(OrderItemType.Handling); LSDecimal curTaxTotal = basket.Items.TotalPrice(OrderItemType.Tax); LSDecimal curItemTotal = curOrderTotal - (curShippingTotal + curHandlingTotal + curTaxTotal); //MAKE SURE OUR BREAKDOWN IS VALID if ((curShippingTotal < 0) || (curHandlingTotal < 0) || (curTaxTotal < 0) || (curItemTotal < 0)) { //THE BREAKDOWN IS INVALID, DO NOT INCLUDE IT IN THE REQUEST curShippingTotal = 0; curHandlingTotal = 0; curTaxTotal = 0; curItemTotal = curOrderTotal; } //SET THE PAYMENT DETAILS expressCheckoutRequest.DoExpressCheckoutPaymentRequestDetails.PaymentDetails = new PaymentDetailsType[1]; expressCheckoutRequest.DoExpressCheckoutPaymentRequestDetails.PaymentDetails[0] = new PaymentDetailsType(); PaymentDetailsType paymentDetails = expressCheckoutRequest.DoExpressCheckoutPaymentRequestDetails.PaymentDetails[0]; paymentDetails.OrderTotal = new BasicAmountType(); paymentDetails.OrderTotal.currencyID = baseCurrencyCode; paymentDetails.OrderTotal.Value = string.Format("{0:##,##0.00}", curOrderTotal); paymentDetails.ItemTotal = new BasicAmountType(); paymentDetails.ItemTotal.currencyID = baseCurrencyCode; paymentDetails.ItemTotal.Value = string.Format("{0:##,##0.00}", curItemTotal); paymentDetails.ShippingTotal = new BasicAmountType(); paymentDetails.ShippingTotal.currencyID = baseCurrencyCode; paymentDetails.ShippingTotal.Value = string.Format("{0:##,##0.00}", curShippingTotal); paymentDetails.HandlingTotal = new BasicAmountType(); paymentDetails.HandlingTotal.currencyID = baseCurrencyCode; paymentDetails.HandlingTotal.Value = string.Format("{0:##,##0.00}", curHandlingTotal); paymentDetails.TaxTotal = new BasicAmountType(); paymentDetails.TaxTotal.currencyID = baseCurrencyCode; paymentDetails.TaxTotal.Value = string.Format("{0:##,##0.00}", curTaxTotal); trace.Write(traceCategory, "Order Total: " + curOrderTotal); trace.Write(traceCategory, "Item Total: " + curItemTotal); trace.Write(traceCategory, "Shipping Total: " + curShippingTotal); trace.Write(traceCategory, "Handling Total: " + curHandlingTotal); trace.Write(traceCategory, "Tax Total: " + curTaxTotal); //SET THE BUTTON SOURCE trace.Write(traceCategory, "Set Button Source"); paymentDetails.ButtonSource = "ablecommerce-EC"; //SET THE NOTIFY URL string notifyUrl = GetStoreUrl() + "/ProcessPayPal.ashx"; trace.Write(traceCategory, "IPN Callback URL: " + notifyUrl); paymentDetails.NotifyURL = notifyUrl; //WE HAVE ALL NECESSARY INFORMATION TO DO EXPRESS CHECKOUT //COMMIT THE ORDER BEFORE SUBMITTING THE PAYPAL TRANSACTION //CREATE THE ABLECOMMERCE PAYMENT ITEM Payment checkoutPayment = new Payment(); checkoutPayment.PaymentMethodId = GetPayPalPaymentMethodId(false); checkoutPayment.Amount = curOrderTotal; checkoutPayment.CurrencyCode = baseCurrencyCode.ToString(); //AT THIS POINT, EXECUTE THE CHECKOUT TO SUBMIT THE ORDER CheckoutRequest checkoutRequest = new CheckoutRequest(checkoutPayment); CheckoutResponse checkoutResponse = basket.Checkout(checkoutRequest); int orderId = checkoutResponse.OrderId; //LOAD THE ORDER AND RE-OBTAIN THE PAYMENT RECORD TO AVOID DATA INCONSISTENCIES Order order = OrderDataSource.Load(orderId); if (order == null) { //EXIT WITH EXCEPTION ErrorType[] customErrorList = new ErrorType[1]; ErrorType customError = new ErrorType(); customError.ErrorCode = "ORDER"; customError.ShortMessage = "Your order could not be completed at this time."; customError.LongMessage = "Your order could not be completed at this time and payment was not processed. " + string.Join(" ", checkoutResponse.WarningMessages.ToArray()); customErrorList[0] = customError; return(new ExpressCheckoutResult(0, string.Empty, customErrorList)); } int findPaymentId = checkoutPayment.PaymentId; foreach (Payment payment in order.Payments) { if (payment.PaymentId == findPaymentId) { checkoutPayment = payment; } } //SET THE DESCRIPTION paymentDetails.OrderDescription = "Order #" + order.OrderNumber.ToString(); paymentDetails.Custom = orderId.ToString(); //EXECUTE PAYPAL REQUEST trace.Write(traceCategory, "Do Request"); DoExpressCheckoutPaymentResponseType expressCheckoutResponse = (DoExpressCheckoutPaymentResponseType)SoapCall("DoExpressCheckoutPayment", expressCheckoutRequest); ErrorType[] responseErrors = null; PaymentStatus finalPaymentStatus = PaymentStatus.Unprocessed; bool isPendingeCheck = false; if (expressCheckoutResponse != null) { if (expressCheckoutResponse.Errors == null) { //CREATE THE PAYPAL TRANSACTION RECORD Transaction checkoutTransaction = new Transaction(); PaymentInfoType paymentInfo = expressCheckoutResponse.DoExpressCheckoutPaymentResponseDetails.PaymentInfo[0]; isPendingeCheck = (paymentInfo.PaymentStatus == PaymentStatusCodeType.Pending && paymentInfo.PendingReason == PendingStatusCodeType.echeck); PaymentStatusCodeType paymentStatus = paymentInfo.PaymentStatus; switch (paymentStatus) { case PaymentStatusCodeType.Completed: case PaymentStatusCodeType.Processed: case PaymentStatusCodeType.Pending: if (isPendingeCheck) { finalPaymentStatus = PaymentStatus.CapturePending; checkoutTransaction.ResponseCode = "PENDING"; checkoutTransaction.ResponseMessage = "echeck"; } else { finalPaymentStatus = (paymentStatus != PaymentStatusCodeType.Pending) ? PaymentStatus.Captured : PaymentStatus.Authorized; } checkoutTransaction.TransactionStatus = TransactionStatus.Successful; break; default: finalPaymentStatus = PaymentStatus.Unprocessed; checkoutTransaction.TransactionStatus = TransactionStatus.Failed; checkoutTransaction.ResponseCode = expressCheckoutResponse.Ack.ToString(); checkoutTransaction.ResponseMessage = paymentStatus.ToString().ToUpperInvariant(); break; } checkoutTransaction.TransactionType = this.UseAuthCapture ? TransactionType.Capture : TransactionType.Authorize; checkoutTransaction.Amount = AlwaysConvert.ToDecimal(paymentInfo.GrossAmount.Value, (Decimal)curOrderTotal); checkoutTransaction.AuthorizationCode = paymentInfo.TransactionID; checkoutTransaction.AVSResultCode = "U"; checkoutTransaction.ProviderTransactionId = paymentInfo.TransactionID; checkoutTransaction.Referrer = context.Request.ServerVariables["HTTP_REFERER"]; checkoutTransaction.PaymentGatewayId = this.PaymentGatewayId; checkoutTransaction.RemoteIP = context.Request.ServerVariables["REMOTE_ADDR"]; checkoutPayment.Transactions.Add(checkoutTransaction); //FIND THE WAITING FOR IPN TRANSACTION AND REMOVE int i = checkoutPayment.Transactions.Count - 1; while (i >= 0) { if (string.IsNullOrEmpty(checkoutPayment.Transactions[i].AuthorizationCode)) { checkoutPayment.Transactions.DeleteAt(i); } i--; } } else { //SOME SORT OF ERROR ATTEMPTING CHECKOUT responseErrors = expressCheckoutResponse.Errors; } } else { //NO RESPONSE, GENERATE CUSTOM ERROR responseErrors = new ErrorType[1]; ErrorType customError = new ErrorType(); customError.ErrorCode = "NORESP"; customError.ShortMessage = "No Response From Server"; customError.LongMessage = "The PayPal service is unavailable at this time."; responseErrors[0] = customError; } trace.Write(traceCategory, "Do Request Done"); //ERRORS IN RESPONSE? if ((responseErrors != null) && (responseErrors.Length > 0)) { //CREATE THE PAYPAL TRANSACTION RECORD FOR ERROR Transaction checkoutTransaction = new Transaction(); finalPaymentStatus = PaymentStatus.Unprocessed; checkoutTransaction.TransactionStatus = TransactionStatus.Failed; checkoutTransaction.Amount = curOrderTotal; checkoutTransaction.AuthorizationCode = string.Empty; checkoutTransaction.Referrer = context.Request.ServerVariables["HTTP_REFERER"]; checkoutTransaction.PaymentGatewayId = this.PaymentGatewayId; checkoutTransaction.RemoteIP = context.Request.ServerVariables["REMOTE_ADDR"]; checkoutTransaction.ResponseCode = responseErrors[0].ShortMessage; checkoutTransaction.ResponseMessage = responseErrors[0].LongMessage; checkoutPayment.Transactions.Add(checkoutTransaction); } //MAKE SURE PAYMENT STATUS IS CORRECT checkoutPayment.ReferenceNumber = paypalSession.Payer; checkoutPayment.PaymentStatus = finalPaymentStatus; if (isPendingeCheck) { checkoutPayment.PaymentStatusReason = "echeck"; } //RECALCULATE THE ORDER STATUS (BUG 6384) AND TRIGGER PAYMENT EVENTS (BUG 8650) order.Save(true, true); //CLEAR THE TOKENS SET IN SESSION paypalSession.Delete(); return(new ExpressCheckoutResult(orderId, string.Empty, responseErrors)); }