이 튜토리얼에서는 PayPal REST API와 C#을 사용하여 결제하는 방법을 보여 드리겠습니다. Ruby, Node.js, Python, PHP 등과 같은 다양한 언어에 제공되는 모든 라이브러리는 매우 유사하므로 여기에 있는 모든 개념은 모든 라이브러리에 적용 가능합니다.
프로젝트 설정
먼저 Visual Studio 2015에서 파일 > 새로 만들기 > 프로젝트 로 MVC 프로젝트를 만들고 ASP.NET Application을 선택했습니다.
새로운 MVC 6을 사용하는 ASP.NET 5 Web Application 템플릿을 선택합니다. 익숙하다면 MVC 5와 유사합니다.
아래 사진에서 볼 수 있듯이 솔루션에 일부 파일과 폴더를 추가했습니다. 주목해야 할 두 가지 주요 사항은 다음과 같습니다.
References에서 이 프로젝트를 Mac OS X 또는 Linux에서 실행할 수 있는 대상 DNX Core 5.0을 제거했지만 필요한 PayPal 라이브러리는 아직 업데이트되지 않았습니다.
컨트롤러를 간단하고 명확하게 유지할 수 있도록 PayPal 호출 논리를 캡슐화할 "서비스" 폴더를 추가했습니다.
NuGet을 사용하여 PayPal SDK를 설치하세요. 솔루션 이름을 마우스 오른쪽 버튼으로 클릭하고 NuGet 패키지 관리를 선택한 다음 "PayPal"을 검색하여 설치하세요.
PayPal 앱 만들기
애플리케이션을 PayPal과 통합하려면 PayPal 개발자로 이동하여 REST API 애플리케이션에서 애플리케이션 만들기를 클릭해야 합니다.
앱 이름을 지정하고 앱과 연결할 샌드박스 개발자 계정을 선택하세요. 테스트 목적으로 http://sandbox.paypal.com으로 이동하여 샌드박스 로그인 세부 정보를 사용하여 로그인하여 테스트 PayPal 계정 및 거래를 볼 수 있습니다.
앱 만들기를 클릭하면 클라이언트 ID와 비밀 토큰이 포함된 확인 화면이 나타납니다.
아래 스크린샷과 같이 clientId 및 clientSecret 토큰을 appsettings.json에 복사합니다.
테스트 결제
PayPal은 테스트를 위한 샌드박스 환경을 제공합니다. 여기에서 테스트 구매자 및 판매자 계정을 생성할 수 있습니다. 가입하고 나면 개발자 계정과 연결된 샌드박스에 기업 계정이 생성됩니다.
새 테스트 계정을 만들려면 개발자 웹사이트에 로그인한 다음 제어판 탭을 클릭하고 샌드박스 > 계정으로 이동하세요. 여기에서 테스트 계정 목록(사용 가능한 경우)을 볼 수 있습니다.
아직 테스트 계정을 만들지 않았다면 오른쪽 상단의 계정 만들기를 클릭하여 최소한 테스트 개인 계정과 테스트 비즈니스 계정을 만드세요.
테스트 계정을 만든 후 이전 양식에서 각 계정에 할당한 테스트 이메일 주소와 비밀번호를 사용하여 www.sandbox.paypal.com을 통해 로그인할 수 있습니다. 이는 개인 테스트 계정을 사용하여 구매할 때 자금이 테스트 비즈니스 계정으로 이체되는지 여부를 테스트하는 데 유용합니다. 이제 PayPal과의 통합을 시작하고 자금이 한 계좌에서 다른 계좌로 이체되는지 테스트할 준비가 되었습니다.
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;
}
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;
}
然后,我在 HomeController 中添加了两个新操作来显示此类付款:
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();
}
// 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" });
}
Python 및 JavaScript의 미래 추세에는 다음이 포함됩니다. 1. Python은 과학 컴퓨팅 분야에서의 위치를 통합하고 AI, 2. JavaScript는 웹 기술의 개발을 촉진하고, 3. 교차 플랫폼 개발이 핫한 주제가되고 4. 성능 최적화가 중점을 둘 것입니다. 둘 다 해당 분야에서 응용 프로그램 시나리오를 계속 확장하고 성능이 더 많은 혁신을 일으킬 것입니다.
개발 환경에서 Python과 JavaScript의 선택이 모두 중요합니다. 1) Python의 개발 환경에는 Pycharm, Jupyternotebook 및 Anaconda가 포함되어 있으며 데이터 과학 및 빠른 프로토 타이핑에 적합합니다. 2) JavaScript의 개발 환경에는 Node.js, VScode 및 Webpack이 포함되어 있으며 프론트 엔드 및 백엔드 개발에 적합합니다. 프로젝트 요구에 따라 올바른 도구를 선택하면 개발 효율성과 프로젝트 성공률이 향상 될 수 있습니다.
예, JavaScript의 엔진 코어는 C로 작성되었습니다. 1) C 언어는 효율적인 성능과 기본 제어를 제공하며, 이는 JavaScript 엔진 개발에 적합합니다. 2) V8 엔진을 예를 들어, 핵심은 C로 작성되며 C의 효율성 및 객체 지향적 특성을 결합하여 C로 작성됩니다.
JavaScript는 웹 페이지의 상호 작용과 역학을 향상시키기 때문에 현대 웹 사이트의 핵심입니다. 1) 페이지를 새로 고치지 않고 콘텐츠를 변경할 수 있습니다. 2) Domapi를 통해 웹 페이지 조작, 3) 애니메이션 및 드래그 앤 드롭과 같은 복잡한 대화식 효과를 지원합니다. 4) 성능 및 모범 사례를 최적화하여 사용자 경험을 향상시킵니다.
C 및 JavaScript는 WebAssembly를 통한 상호 운용성을 달성합니다. 1) C 코드는 WebAssembly 모듈로 컴파일되어 컴퓨팅 전력을 향상시키기 위해 JavaScript 환경에 도입됩니다. 2) 게임 개발에서 C는 물리 엔진 및 그래픽 렌더링을 처리하며 JavaScript는 게임 로직 및 사용자 인터페이스를 담당합니다.
JavaScript는 웹 사이트, 모바일 응용 프로그램, 데스크탑 응용 프로그램 및 서버 측 프로그래밍에서 널리 사용됩니다. 1) 웹 사이트 개발에서 JavaScript는 HTML 및 CSS와 함께 DOM을 운영하여 동적 효과를 달성하고 jQuery 및 React와 같은 프레임 워크를 지원합니다. 2) 반응 및 이온 성을 통해 JavaScript는 크로스 플랫폼 모바일 애플리케이션을 개발하는 데 사용됩니다. 3) 전자 프레임 워크를 사용하면 JavaScript가 데스크탑 애플리케이션을 구축 할 수 있습니다. 4) node.js는 JavaScript가 서버 측에서 실행되도록하고 동시 요청이 높은 높은 요청을 지원합니다.
Python은 데이터 과학 및 자동화에 더 적합한 반면 JavaScript는 프론트 엔드 및 풀 스택 개발에 더 적합합니다. 1. Python은 데이터 처리 및 모델링을 위해 Numpy 및 Pandas와 같은 라이브러리를 사용하여 데이터 과학 및 기계 학습에서 잘 수행됩니다. 2. 파이썬은 간결하고 자동화 및 스크립팅이 효율적입니다. 3. JavaScript는 프론트 엔드 개발에 없어서는 안될 것이며 동적 웹 페이지 및 단일 페이지 응용 프로그램을 구축하는 데 사용됩니다. 4. JavaScript는 Node.js를 통해 백엔드 개발에 역할을하며 전체 스택 개발을 지원합니다.
C와 C는 주로 통역사와 JIT 컴파일러를 구현하는 데 사용되는 JavaScript 엔진에서 중요한 역할을합니다. 1) C는 JavaScript 소스 코드를 구문 분석하고 추상 구문 트리를 생성하는 데 사용됩니다. 2) C는 바이트 코드 생성 및 실행을 담당합니다. 3) C는 JIT 컴파일러를 구현하고 런타임에 핫스팟 코드를 최적화하고 컴파일하며 JavaScript의 실행 효율을 크게 향상시킵니다.