In this tutorial, I will show you how to make a payment using the PayPal REST API and C#. All the libraries they provide for different languages like Ruby, Node.js, Python, PHP, etc. are very similar, so all the concepts here are applicable to all libraries.
Project settings
First, I created an MVC project in Visual Studio 2015: File > New > Project and selected ASP.NET Application.
Select the ASP.NET 5 Web Application template, which uses the new MVC 6. It's similar to MVC 5 if you're familiar with it.
As you can see in the photo below, I have added some files and folders to the solution. The two main things to note are:
In Reference I removed the target DNX Core 5.0, which allows us to run this project in Mac OS X or Linux, but the PayPal libraries we need have not been updated yet.
I added the "Services" folder where I will encapsulate the logic of the PayPal calls so we can keep the controller clean and simple.
Use NuGet to install the PayPal SDK. Right-click the solution name and select Manage NuGet Packages, then search for "PayPal" and install it.
Create PayPal App
To integrate our application with PayPal, we need to navigate to PayPal Developers and under REST API Applications, click Create Application.
Name your app and select a sandbox developer account to associate with the app. For testing purposes, we can navigate to http://sandbox.paypal.com and log in using the sandbox login details to view a test PayPal account and transactions.
After clicking Create Application we will see a confirmation screen containing the Client ID and Secret Token.
Copy the clientId and clientSecret tokens to appsettings.json as shown in the screenshot below:
Test Payment
PayPal provides a sandbox environment for testing. From there you can create test buyer and seller accounts. After signing up, you will have an enterprise account in the sandbox tied to your developer account.
To create a new test account, log in to the developer website, then click the Control Panel tab and navigate to Sandbox > Accounts. You can see a list of test accounts (if available) here:
If you haven't created a test account yet, go ahead and click Create AccountIn the upper right corner, create at least one test personal account and one test business account.
After you create a test account, you can log in through www.sandbox.paypal.com using the test email address and password you assigned to each account on the previous form. This is useful for testing whether funds will be transferred to your Test Business Account when you make a purchase using your Personal Test Account. Now you're ready to start integrating with PayPal and test whether funds are transferred from one account to another.
Single PayPal Payment
PayPal offers different payment methods. You can use direct credit card payments, which means your customers can't see the PayPal login page or summary - it all happens on your website. To do this you need to be PCI compliant and I recommend using Stripe as you only need SSL using their JavaScript library. To pay via PayPal, on the other hand, there are three steps:
Specify payment information to create a payment.
Get Payment Approval Redirect your customer to PayPal to approve the transaction.
After PayPal redirects your customer back to your website, Execute the payment to get the funds.
In the Services folder of my MVC project, I created the PayPalPaymentService class and added the following methods in it:
public static Payment CreatePayment(string baseUrl, string intent)
{
// ### Api Context
// Pass in a `APIContext` object to authenticate
// the call and to send a unique request id
// (that ensures idempotency). The SDK generates
// a request id if you do not pass one explicitly.
var apiContext = PayPalConfiguration.GetAPIContext();
// Payment Resource
var payment = new Payment()
{
intent = intent, // `sale` or `authorize`
payer = new Payer() { payment_method = "paypal" },
transactions = GetTransactionsList(),
redirect_urls = GetReturnUrls(baseUrl, intent)
};
// Create a payment using a valid APIContext
var createdPayment = payment.Create(apiContext);
return createdPayment;
}
private static List<Transaction> GetTransactionsList()
{
// A transaction defines the contract of a payment
// what is the payment for and who is fulfilling it.
var transactionList = new List<Transaction>();
// The Payment creation API requires a list of Transaction;
// add the created Transaction to a List
transactionList.Add(new Transaction()
{
description = "Transaction description.",
invoice_number = GetRandomInvoiceNumber(),
amount = new Amount()
{
currency = "USD",
total = "100.00", // Total must be equal to sum of shipping, tax and subtotal.
details = new Details() // Details: Let's you specify details of a payment amount.
{
tax = "15",
shipping = "10",
subtotal = "75"
}
},
item_list = new ItemList()
{
items = new List<Item>()
{
new Item()
{
name = "Item Name",
currency = "USD",
price = "15",
quantity = "5",
sku = "sku"
}
}
}
});
return transactionList;
}
private static RedirectUrls GetReturnUrls(string baseUrl, string intent)
{
var returnUrl = intent == "sale" ? "/Home/PaymentSuccessful" : "/Home/AuthorizeSuccessful";
// Redirect URLS
// These URLs will determine how the user is redirected from PayPal
// once they have either approved or canceled the payment.
return new RedirectUrls()
{
cancel_url = baseUrl + "/Home/PaymentCancelled",
return_url = baseUrl + returnUrl
};
}
public static Payment ExecutePayment(string paymentId, string payerId)
{
// ### Api Context
// Pass in a `APIContext` object to authenticate
// the call and to send a unique request id
// (that ensures idempotency). The SDK generates
// a request id if you do not pass one explicitly.
var apiContext = PayPalConfiguration.GetAPIContext();
var paymentExecution = new PaymentExecution() { payer_id = payerId };
var payment = new Payment() { id = paymentId };
// Execute the payment.
var executedPayment = payment.Execute(apiContext, paymentExecution);
return executedPayment;
}
Some parameters are passed in this call:
Intent: Three possible values: "sales" for immediate payment, "authorization" for authorization to capture payment later, or "order" for creating an order. When you are authorized to charge a payment later, you have a 3-day guarantee, but you can try to collect the payment up to 29 days later.
Payer: The source of funds for this payment, the payment method used - PayPal Wallet Payment, Bank Direct Debit, or Direct Credit Card.
Transaction: is used to specify the payment amount and optionally specify the items to be paid. If required, you can also specify subtotals, shipping costs, and taxes.
Redirect URL: Specify the URL to which PayPal redirects your customers after a transaction so that you can update the database and display a confirmation message.
The previous functionality can be used from the controller as follows:
public IActionResult CreatePayment()
{
var payment = PayPalPaymentService.CreatePayment(GetBaseUrl(), "sale");
return Redirect(payment.GetApprovalUrl());
}
public IActionResult PaymentCancelled()
{
// TODO: Handle cancelled payment
return RedirectToAction("Error");
}
public IActionResult PaymentSuccessful(string paymentId, string token, string PayerID)
{
// Execute Payment
var payment = PayPalPaymentService.ExecutePayment(paymentId, PayerID);
return View();
}
As you can see, I created three actions:
CreatePayment: This is the action that triggers the payment. It calls PayPal to create the payment and then redirects the user to PayPal to approve the transaction.
Payment Success: This is what PayPal does to redirect the customer back after a successful payment. At this point, we can execute the payment and transfer the funds to our merchant account.
PaymentCancelled: Redirect the user from PayPal to this action if the user cancels the approval process. At this point, you may want to give the customer the option to try again or contact you.
Authorize payment to get later
This scenario is very similar to the previous case. You may want to use this method if you are trying to pre-order a product that is not yet available. The steps to get this payment are:
Authorize payment: The "intent" parameter of this call should be "authorize".
Getting paid: Remember, authorization is guaranteed for up to 3 days, but you can try to get paid for up to 29 days.
To implement this type of payment, I just added a new method in the PayPalPaymentService class to capture the payment:
public static Capture CapturePayment(string paymentId)
{
var apiContext = PayPalConfiguration.GetAPIContext();
var payment = Payment.Get(apiContext, paymentId);
var auth = payment.transactions[0].related_resources[0].authorization;
// Specify an amount to capture. By setting 'is_final_capture' to true, all remaining funds held by the authorization will be released from the funding instrument.
var capture = new Capture()
{
amount = new Amount()
{
currency = "USD",
total = "4.54"
},
is_final_capture = true
};
// Capture an authorized payment by POSTing to
// URI v1/payments/authorization/{authorization_id}/capture
var responseCapture = auth.Capture(apiContext, capture);
return responseCapture;
}
Then I added two new actions in HomeController to display this type of payment:
public IActionResult AuthorizePayment()
{
var payment = PayPalPaymentService.CreatePayment(GetBaseUrl(), "authorize");
return Redirect(payment.GetApprovalUrl());
}
public IActionResult AuthorizeSuccessful(string paymentId, string token, string PayerID)
{
// Capture Payment
var capture = PayPalPaymentService.CapturePayment(paymentId);
return View();
}
AuthorizePayment is the action that triggers the payment. It is very similar to the previous "CreatePayment" function, but in this case we pass "authorize" as the intent parameter.
AuthorizeSuccessful is the action that will redirect your customer after successfully approving a PayPal payment. At this point, I'm capturing the payment, but you can save the paymentId in the database and capture the payment when needed.
In these code examples, I have hardcoded the payment variable values for simplicity. In your real application you would probably wrap them in a method that takes all these values as variables so that everything can be set and reused dynamically.
subscription
This is called a "Billing Plan" in PayPal - you can create a recurring payment plan and subscribe your customers to a billing plan by creating a Billing Agreement. Using the PayPal REST API, you can create, update, or delete billing plans; this is something you might use if you wanted to build an admin panel to manage these things for your business.
The steps to create a recurring charge to a customer are as follows:
Create a billing plan and activate it. After you create a billing plan, it will have a "Created" status. It needs to be activated by issuing a PATCH request.
Create Settlement Agreement and Execute: The response to the "Create Settlement Agreement" call includes links to approval_url and execute_url. We need to obtain approval of the billing agreement and then execute the billing agreement.
Billing Plan
Create billing plan
Create a billing plan that defines the billing cycle. This is a summary of the parameters we need to pass when creating the plan.
// Define the plan and attach the payment definitions and merchant preferences.
// More Information: https://developer.paypal.com/webapps/developer/docs/api/#create-a-plan
var billingPlan = new Plan
{
name = "Tuts+ Plus",
description = "Monthly plan for courses.",
type = "fixed",
// Define the merchant preferences.
// More Information: https://developer.paypal.com/webapps/developer/docs/api/#merchantpreferences-object
merchant_preferences = new MerchantPreferences()
{
setup_fee = GetCurrency("0"), // $0
return_url = "returnURL", // Retrieve from config
cancel_url = "cancelURL", // Retrieve from config
auto_bill_amount = "YES",
initial_fail_amount_action = "CONTINUE",
max_fail_attempts = "0"
},
payment_definitions = new List<PaymentDefinition>
{
// Define a trial plan that will only charge $9.99 for the first
// month. After that, the standard plan will take over for the
// remaining 11 months of the year.
new PaymentDefinition()
{
name = "Trial Plan",
type = "TRIAL",
frequency = "MONTH",
frequency_interval = "1",
amount = GetCurrency("0"), // Free for the 1st month
cycles = "1",
charge_models = new List<ChargeModel>
{
new ChargeModel()
{
type = "TAX",
amount = GetCurrency("1.65") // If we need to charge Tax
},
new ChargeModel()
{
type = "SHIPPING",
amount = GetCurrency("9.99") // If we need to charge for Shipping
}
}
},
// Define the standard payment plan. It will represent a monthly
// plan for $19.99 USD that charges once month for 11 months.
new PaymentDefinition
{
name = "Standard Plan",
type = "REGULAR",
frequency = "MONTH",
frequency_interval = "1",
amount = GetCurrency("15.00"),
// > NOTE: For `IFNINITE` type plans, `cycles` should be 0 for a `REGULAR` `PaymentDefinition` object.
cycles = "11",
charge_models = new List<ChargeModel>
{
new ChargeModel
{
type = "TAX",
amount = GetCurrency("2.47")
},
new ChargeModel()
{
type = "SHIPPING",
amount = GetCurrency("9.99")
}
}
}
}
};
// Get PayPal Config
var apiContext = PayPalConfiguration.GetAPIContext();
// Create Plan
plan.Create(apiContext);
新创建的结算计划处于 CREATED 状态。将其激活为“活动”状态,以便您的客户可以订阅该计划。要激活该计划,我们需要发出 PATCH 请求:
// Activate the plan
var patchRequest = new PatchRequest()
{
new Patch()
{
op = "replace",
path = "/",
value = new Plan() { state = "ACTIVE" }
}
};
plan.Update(apiContext, patchRequest);
如您所见,PayPal 库是其 REST API 的直接包装器,这很好,但与 Stripe 等其他 API 相比,该 API 也非常复杂。因此,将所有 PayPal 通信包装在对象中,为我们的应用程序提供更清晰、更简单的 API,这确实是一个不错的选择。在这里您可以看到封装在多个带有参数的函数中的代码的样子:
public static Plan CreatePlanObject(string planName, string planDescription, string returnUrl, string cancelUrl,
string frequency, int frequencyInterval, decimal planPrice,
decimal shippingAmount = 0, decimal taxPercentage = 0, bool trial = false, int trialLength = 0, decimal trialPrice = 0)
{
// Define the plan and attach the payment definitions and merchant preferences.
// More Information: https://developer.paypal.com/docs/rest/api/payments.billing-plans/
return new Plan
{
name = planName,
description = planDescription,
type = PlanType.Fixed,
// Define the merchant preferences.
// More Information: https://developer.paypal.com/webapps/developer/docs/api/#merchantpreferences-object
merchant_preferences = new MerchantPreferences()
{
setup_fee = GetCurrency("1"),
return_url = returnUrl,
cancel_url = cancelUrl,
auto_bill_amount = "YES",
initial_fail_amount_action = "CONTINUE",
max_fail_attempts = "0"
},
payment_definitions = GetPaymentDefinitions(trial, trialLength, trialPrice, frequency, frequencyInterval, planPrice, shippingAmount, taxPercentage)
};
}
private static List<PaymentDefinition> GetPaymentDefinitions(bool trial, int trialLength, decimal trialPrice,
string frequency, int frequencyInterval, decimal planPrice, decimal shippingAmount, decimal taxPercentage)
{
var paymentDefinitions = new List<PaymentDefinition>();
if (trial)
{
// Define a trial plan that will charge 'trialPrice' for 'trialLength'
// After that, the standard plan will take over.
paymentDefinitions.Add(
new PaymentDefinition()
{
name = "Trial",
type = "TRIAL",
frequency = frequency,
frequency_interval = frequencyInterval.ToString(),
amount = GetCurrency(trialPrice.ToString()),
cycles = trialLength.ToString(),
charge_models = GetChargeModels(trialPrice, shippingAmount, taxPercentage)
});
}
// Define the standard payment plan. It will represent a 'frequency' (monthly, etc)
// plan for 'planPrice' that charges 'planPrice' (once a month) for #cycles.
var regularPayment = new PaymentDefinition
{
name = "Standard Plan",
type = "REGULAR",
frequency = frequency,
frequency_interval = frequencyInterval.ToString(),
amount = GetCurrency(planPrice.ToString()),
// > NOTE: For `IFNINITE` type plans, `cycles` should be 0 for a `REGULAR` `PaymentDefinition` object.
cycles = "11",
charge_models = GetChargeModels(trialPrice, shippingAmount, taxPercentage)
};
paymentDefinitions.Add(regularPayment);
return paymentDefinitions;
}
private static List<ChargeModel> GetChargeModels(decimal planPrice, decimal shippingAmount, decimal taxPercentage)
{
// Create the Billing Plan
var chargeModels = new List<ChargeModel>();
if (shippingAmount > 0)
{
chargeModels.Add(new ChargeModel()
{
type = "SHIPPING",
amount = GetCurrency(shippingAmount.ToString())
});
}
if (taxPercentage > 0)
{
chargeModels.Add(new ChargeModel()
{
type = "TAX",
amount = GetCurrency(String.Format("{0:f2}", planPrice * taxPercentage / 100))
});
}
return chargeModels;
}
更新结算计划
您可以通过提出“PATCH”请求来更新现有结算方案的信息。这是一个包装该调用的函数:
public static void UpdateBillingPlan(string planId, string path, object value)
{
// PayPal Authentication tokens
var apiContext = PayPalConfiguration.GetAPIContext();
// Retrieve Plan
var plan = Plan.Get(apiContext, planId);
// Activate the plan
var patchRequest = new PatchRequest()
{
new Patch()
{
op = "replace",
path = path,
value = value
}
};
plan.Update(apiContext, patchRequest);
}
要更新计费计划描述,我们可以调用此函数并传递正确的参数:
UpdateBillingPlan(
planId: "P-5FY40070P6526045UHFWUVEI",
path: "/",
value: new Plan { description = "new description" });
public static void ExecuteBillingAgreement(string token)
{
// PayPal Authentication tokens
var apiContext = PayPalConfiguration.GetAPIContext();
var agreement = new Agreement() { token = token };
var executedAgreement = agreement.Execute(apiContext);
}
暂停计费协议
使用此方法暂停协议:
public static void SuspendBillingAgreement(string agreementId)
{
var apiContext = PayPalConfiguration.GetAPIContext();
var agreement = new Agreement() { id = agreementId };
agreement.Suspend(apiContext, new AgreementStateDescriptor()
{ note = "Suspending the agreement" });
}
重新激活计费协议
这个与上一个非常相似:
public static void ReactivateBillingAgreement(string agreementId)
{
var apiContext = PayPalConfiguration.GetAPIContext();
var agreement = new Agreement() { id = agreementId };
agreement.ReActivate(apiContext, new AgreementStateDescriptor()
{ note = "Reactivating the agreement" });
}
取消计费协议
使用此功能取消计划:
public static void CancelBillingAgreement(string agreementId)
{
var apiContext = PayPalConfiguration.GetAPIContext();
var agreement = new Agreement() { id = agreementId };
agreement.Cancel(apiContext, new AgreementStateDescriptor()
{ note = "Cancelling the agreement" });
}
这是人们用来与 PayPal 集成的最常用功能的概述。他们的 API 比本文中解释的集成方法要大得多 - 您还可以发放退款和部分退款,并且他们针对本文涵盖的示例中的边缘情况提供了许多不同的选项。如果您有兴趣获得有关任何特定集成的更多详细信息,请在评论中留下建议。
The above is the detailed content of Deep Dive: PayPal Integration Part 2: PayPal REST API. For more information, please follow other related articles on the PHP Chinese website!
Statement
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Detailed explanation of JavaScript string replacement method and FAQ
This article will explore two ways to replace string characters in JavaScript: internal JavaScript code and internal HTML for web pages.
Replace string inside JavaScript code
The most direct way is to use the replace() method:
str = str.replace("find","replace");
This method replaces only the first match. To replace all matches, use a regular expression and add the global flag g:
str = str.replace(/fi
This tutorial shows you how to integrate a custom Google Search API into your blog or website, offering a more refined search experience than standard WordPress theme search functions. It's surprisingly easy! You'll be able to restrict searches to y
So here you are, ready to learn all about this thing called AJAX. But, what exactly is it? The term AJAX refers to a loose grouping of technologies that are used to create dynamic, interactive web content.
The term AJAX, originally coined by Jesse J
This article series was rewritten in mid 2017 with up-to-date information and fresh examples.
In this JSON example, we will look at how we can store simple values in a file using JSON format.
Using the key-value pair notation, we can store any kind
Leverage jQuery for Effortless Web Page Layouts: 8 Essential Plugins
jQuery simplifies web page layout significantly. This article highlights eight powerful jQuery plugins that streamline the process, particularly useful for manual website creation
Core points
This in JavaScript usually refers to an object that "owns" the method, but it depends on how the function is called.
When there is no current object, this refers to the global object. In a web browser, it is represented by window.
When calling a function, this maintains the global object; but when calling an object constructor or any of its methods, this refers to an instance of the object.
You can change the context of this using methods such as call(), apply(), and bind(). These methods call the function using the given this value and parameters.
JavaScript is an excellent programming language. A few years ago, this sentence was
jQuery is a great JavaScript framework. However, as with any library, sometimes it’s necessary to get under the hood to discover what’s going on. Perhaps it’s because you’re tracing a bug or are just curious about how jQuery achieves a particular UI
This post compiles helpful cheat sheets, reference guides, quick recipes, and code snippets for Android, Blackberry, and iPhone app development. No developer should be without them!
Touch Gesture Reference Guide (PDF)
A valuable resource for desig
Safe Exam Browser is a secure browser environment for taking online exams securely. This software turns any computer into a secure workstation. It controls access to any utility and prevents students from using unauthorized resources.