예제 #1
0
        public async Task <IActionResult> PostIpn()
        {
            if (HttpContext?.Request?.Query == null)
            {
                return(new BadRequestResult());
            }

            var key = HttpContext.Request.Query.ContainsKey("key") ?
                      HttpContext.Request.Query["key"].ToString() : null;

            if (key != _billingSettings.PayPal.WebhookKey)
            {
                return(new BadRequestResult());
            }

            string body = null;

            using (var reader = new StreamReader(HttpContext.Request.Body, Encoding.UTF8))
            {
                body = await reader.ReadToEndAsync();
            }

            if (string.IsNullOrWhiteSpace(body))
            {
                return(new BadRequestResult());
            }

            var verified = await _paypalIpnClient.VerifyIpnAsync(body);

            if (!verified)
            {
                _logger.LogWarning("Unverified IPN received.");
                return(new BadRequestResult());
            }

            var ipnTransaction = new PayPalIpnClient.IpnTransaction(body);

            if (ipnTransaction.TxnType != "web_accept" && ipnTransaction.TxnType != "merch_pmt" &&
                ipnTransaction.PaymentStatus != "Refunded")
            {
                // Only processing billing agreement payments, buy now button payments, and refunds for now.
                return(new OkResult());
            }

            if (ipnTransaction.ReceiverId != _billingSettings.PayPal.BusinessId)
            {
                _logger.LogWarning("Receiver was not proper business id. " + ipnTransaction.ReceiverId);
                return(new BadRequestResult());
            }

            if (ipnTransaction.PaymentStatus == "Refunded" && ipnTransaction.ParentTxnId == null)
            {
                // Refunds require parent transaction
                return(new OkResult());
            }

            if (ipnTransaction.PaymentType == "echeck" && ipnTransaction.PaymentStatus != "Refunded")
            {
                // Not accepting eChecks, unless it is a refund
                _logger.LogWarning("Got an eCheck payment. " + ipnTransaction.TxnId);
                return(new OkResult());
            }

            if (ipnTransaction.McCurrency != "USD")
            {
                // Only process USD payments
                _logger.LogWarning("Received a payment not in USD. " + ipnTransaction.TxnId);
                return(new OkResult());
            }

            var ids = ipnTransaction.GetIdsFromCustom();

            if (!ids.Item1.HasValue && !ids.Item2.HasValue)
            {
                return(new OkResult());
            }

            if (ipnTransaction.PaymentStatus == "Completed")
            {
                var transaction = await _transactionRepository.GetByGatewayIdAsync(
                    GatewayType.PayPal, ipnTransaction.TxnId);

                if (transaction != null)
                {
                    _logger.LogWarning("Already processed this completed transaction. #" + ipnTransaction.TxnId);
                    return(new OkResult());
                }

                var isAccountCredit = ipnTransaction.IsAccountCredit();
                try
                {
                    var tx = new Transaction
                    {
                        Amount            = ipnTransaction.McGross,
                        CreationDate      = ipnTransaction.PaymentDate,
                        OrganizationId    = ids.Item1,
                        UserId            = ids.Item2,
                        Type              = isAccountCredit ? TransactionType.Credit : TransactionType.Charge,
                        Gateway           = GatewayType.PayPal,
                        GatewayId         = ipnTransaction.TxnId,
                        PaymentMethodType = PaymentMethodType.PayPal,
                        Details           = ipnTransaction.TxnId
                    };
                    await _transactionRepository.CreateAsync(tx);

                    if (isAccountCredit)
                    {
                        string billingEmail = null;
                        if (tx.OrganizationId.HasValue)
                        {
                            var org = await _organizationRepository.GetByIdAsync(tx.OrganizationId.Value);

                            if (org != null)
                            {
                                billingEmail = org.BillingEmailAddress();
                                if (await _paymentService.CreditAccountAsync(org, tx.Amount))
                                {
                                    await _organizationRepository.ReplaceAsync(org);
                                }
                            }
                        }
                        else
                        {
                            var user = await _userRepository.GetByIdAsync(tx.UserId.Value);

                            if (user != null)
                            {
                                billingEmail = user.BillingEmailAddress();
                                if (await _paymentService.CreditAccountAsync(user, tx.Amount))
                                {
                                    await _userRepository.ReplaceAsync(user);
                                }
                            }
                        }

                        if (!string.IsNullOrWhiteSpace(billingEmail))
                        {
                            await _mailService.SendAddedCreditAsync(billingEmail, tx.Amount);
                        }
                    }
                }
                // Catch foreign key violations because user/org could have been deleted.
                catch (SqlException e) when(e.Number == 547)
                {
                }
            }
            else if (ipnTransaction.PaymentStatus == "Refunded")
            {
                var refundTransaction = await _transactionRepository.GetByGatewayIdAsync(
                    GatewayType.PayPal, ipnTransaction.TxnId);

                if (refundTransaction != null)
                {
                    _logger.LogWarning("Already processed this refunded transaction. #" + ipnTransaction.TxnId);
                    return(new OkResult());
                }

                var parentTransaction = await _transactionRepository.GetByGatewayIdAsync(
                    GatewayType.PayPal, ipnTransaction.ParentTxnId);

                if (parentTransaction == null)
                {
                    _logger.LogWarning("Parent transaction was not found. " + ipnTransaction.TxnId);
                    return(new BadRequestResult());
                }

                var refundAmount    = System.Math.Abs(ipnTransaction.McGross);
                var remainingAmount = parentTransaction.Amount -
                                      parentTransaction.RefundedAmount.GetValueOrDefault();
                if (refundAmount > 0 && !parentTransaction.Refunded.GetValueOrDefault() &&
                    remainingAmount >= refundAmount)
                {
                    parentTransaction.RefundedAmount =
                        parentTransaction.RefundedAmount.GetValueOrDefault() + refundAmount;
                    if (parentTransaction.RefundedAmount == parentTransaction.Amount)
                    {
                        parentTransaction.Refunded = true;
                    }

                    await _transactionRepository.ReplaceAsync(parentTransaction);

                    await _transactionRepository.CreateAsync(new Transaction
                    {
                        Amount            = refundAmount,
                        CreationDate      = ipnTransaction.PaymentDate,
                        OrganizationId    = ids.Item1,
                        UserId            = ids.Item2,
                        Type              = TransactionType.Refund,
                        Gateway           = GatewayType.PayPal,
                        GatewayId         = ipnTransaction.TxnId,
                        PaymentMethodType = PaymentMethodType.PayPal,
                        Details           = ipnTransaction.TxnId
                    });
                }
            }

            return(new OkResult());
        }
예제 #2
0
        public async Task <IActionResult> PostIpn([FromQuery] string key)
        {
            if (key != _billingSettings.PayPal.WebhookKey)
            {
                return(new BadRequestResult());
            }

            if (HttpContext?.Request == null)
            {
                return(new BadRequestResult());
            }

            string body = null;

            using (var reader = new StreamReader(HttpContext.Request.Body, Encoding.UTF8))
            {
                body = await reader.ReadToEndAsync();
            }

            if (string.IsNullOrWhiteSpace(body))
            {
                return(new BadRequestResult());
            }

            var verified = await _paypalIpnClient.VerifyIpnAsync(body);

            if (!verified)
            {
                return(new BadRequestResult());
            }

            var ipnTransaction = new PayPalIpnClient.IpnTransaction(body);

            if (ipnTransaction.ReceiverId != _billingSettings.PayPal.BusinessId || ipnTransaction.McCurrency != "USD")
            {
                return(new BadRequestResult());
            }

            var ids = ipnTransaction.GetIdsFromCustom();

            if (!ids.Item1.HasValue && !ids.Item2.HasValue)
            {
                return(new OkResult());
            }

            // Only processing credits via IPN for now
            if (!ipnTransaction.IsAccountCredit())
            {
                return(new OkResult());
            }

            if (ipnTransaction.PaymentStatus == "Completed")
            {
                var transaction = await _transactionRepository.GetByGatewayIdAsync(
                    GatewayType.PayPal, ipnTransaction.TxnId);

                if (transaction == null)
                {
                    try
                    {
                        var tx = new Transaction
                        {
                            Amount            = ipnTransaction.McGross,
                            CreationDate      = ipnTransaction.PaymentDate,
                            OrganizationId    = ids.Item1,
                            UserId            = ids.Item2,
                            Type              = TransactionType.Charge,
                            Gateway           = GatewayType.PayPal,
                            GatewayId         = ipnTransaction.TxnId,
                            PaymentMethodType = PaymentMethodType.PayPal,
                            Details           = ipnTransaction.TxnId
                        };
                        await _transactionRepository.CreateAsync(tx);

                        if (ipnTransaction.IsAccountCredit())
                        {
                            if (tx.OrganizationId.HasValue)
                            {
                                var org = await _organizationRepository.GetByIdAsync(tx.OrganizationId.Value);

                                if (org != null)
                                {
                                    if (await _paymentService.CreditAccountAsync(org, tx.Amount))
                                    {
                                        await _organizationRepository.ReplaceAsync(org);
                                    }
                                }
                            }
                            else
                            {
                                var user = await _userRepository.GetByIdAsync(tx.UserId.Value);

                                if (user != null)
                                {
                                    if (await _paymentService.CreditAccountAsync(user, tx.Amount))
                                    {
                                        await _userRepository.ReplaceAsync(user);
                                    }
                                }
                            }

                            // TODO: Send email about credit added?
                        }
                    }
                    // Catch foreign key violations because user/org could have been deleted.
                    catch (SqlException e) when(e.Number == 547)
                    {
                    }
                }
            }
            else if (ipnTransaction.PaymentStatus == "Refunded")
            {
                var refundTransaction = await _transactionRepository.GetByGatewayIdAsync(
                    GatewayType.PayPal, ipnTransaction.TxnId);

                if (refundTransaction == null)
                {
                    var parentTransaction = await _transactionRepository.GetByGatewayIdAsync(
                        GatewayType.PayPal, ipnTransaction.ParentTxnId);

                    if (parentTransaction == null)
                    {
                        return(new BadRequestResult());
                    }

                    var refundAmount    = System.Math.Abs(ipnTransaction.McGross);
                    var remainingAmount = parentTransaction.Amount -
                                          parentTransaction.RefundedAmount.GetValueOrDefault();
                    if (refundAmount > 0 && !parentTransaction.Refunded.GetValueOrDefault() &&
                        remainingAmount >= refundAmount)
                    {
                        parentTransaction.RefundedAmount =
                            parentTransaction.RefundedAmount.GetValueOrDefault() + refundAmount;
                        if (parentTransaction.RefundedAmount == parentTransaction.Amount)
                        {
                            parentTransaction.Refunded = true;
                        }

                        await _transactionRepository.ReplaceAsync(parentTransaction);

                        await _transactionRepository.CreateAsync(new Transaction
                        {
                            Amount            = ipnTransaction.McGross,
                            CreationDate      = ipnTransaction.PaymentDate,
                            OrganizationId    = ids.Item1,
                            UserId            = ids.Item2,
                            Type              = TransactionType.Refund,
                            Gateway           = GatewayType.PayPal,
                            GatewayId         = ipnTransaction.TxnId,
                            PaymentMethodType = PaymentMethodType.PayPal,
                            Details           = ipnTransaction.TxnId
                        });
                    }
                }
            }

            return(new OkResult());
        }