Paypal 支付功能的 C# .NET / JS 实现
说明
最近用到了 Paypal 支付功能,英语一般般的我也不得不硬着头皮踩一踩这样的坑。经过近乎半个月的作,终于实现了简单的支付功能,那么首先就说说使用 Paypal 必定要知道的几点(当前日期 2018年08月07日):
1. 你应该知道 Paypal 支付功能是支持银联卡的,但是不支持中国买家账号支付给中国卖家账号
2. Paypal 接口有两套,切记,产品环境和 sandbox 测试环境不同
3. 测试账号同样不能使用中国账号给中国账号付款
4. 如果你仅仅想具有固定金额的支付按钮,用你的 Paypal 商家账号登录官网,配置页里面完全可以配置出固定的支付按钮,然后 Copy 对应的 Html 到你的页面就 OK 了,也就没有必要通过更复杂的方式去支付了
5. 如果你必须动态价格和商品信息、或者你要学习基本的 Paypal 接口的话,那么就请静静的往下看吧
6. 真实环境支付 Paypal 每一笔都需要收取商家账号手续费的,并且手续费不低,如果你用真实环境测试,那么一定要记得每一笔都申请退款吧,退款很方便,商家后台就能直接发起,退款几乎是实时的。
Paypal 费用说明:https://www.paypal.com/businesswallet/fees/paypal-fees
相关资料
Paypal 官方地址:https://www.paypal.com/
Paypal 官方测试地址:https://www.sandbox.paypal.com
Paypal 开发者中心:https://developer.paypal.com/
Paypal API: https://api.paypal.com
Paypal sandbox API: https://api.sandbox.paypal.com
Paypal Checkout JS 支付模式
模式图片:
模式说明:
Checkout JS 模式是一种前端实现,使用官方提供的 Checkout.js SDK 实现支付,不需要自己写直接调用接口的代码,相对而言也挺简单,但是如果你想检测支付是否成功,你应当通过调用接口的方式验证了。
支付部分代码:
<div id="paypal-button"></div>
<script src="https://www.paypalobjects.com/api/checkout.js"></script>
<script type="text/javascript">
paypal.Button.render({
env: 'production', // production or sandbox 表示产品环境还是测试环境
client: {
production: '', // 产品环境,值为字符串,配置实际商家号的 ClientId
// sandbox: '', // 测试环境,值为字符串,配置商家测试号的 ClientId
},
style: {
size: 'medium',
color: 'black',
shape: 'pill',
label: 'paypal',
tagline: 'false',
fundingicons: 'true'
},
commit: true,
payment: function (data, actions) {
return actions.payment.create({
transactions: [
{
amount: {
total: "0.01",
currency: "USD"
},
description: "测试商品描述",
custom: "X00002"
}
],
redirect_urls: {
return_url: 'http://localhost:4478/Success.aspx?type=js',
cancel_url: 'http://localhost:4478/Cancel.aspx'
}
});
},
onAuthorize: function (data, actions) {
return actions.payment.execute()
.then(function () {
actions.redirect();
});
},
onCancel: function (data, actions) {
actions.redirect();
}
}, '#paypal-button');
</script>
如果你需要在支付跳转的成功页再次验证一下是否支付成功,你需要自己调用官方提供的 RESTful API,参见下文的 RESTful API 支付模式
RESTful API 支付模式
说明
接口的方式很常见,和支付宝的接口类似,只是使用了 RESTful API 的模式,采用了 Basic Auth 的加密方式。使用接口的模式很常规,我们在页面点击按钮调用支付接口,弹出支付页,支付成功跳转到成功页面,成功页面再调用确认支付接口确认结果。
支付接口调用:
using System;
using System.Text;
using System.Web.Script.Serialization;
using cn.lovelong.Paypal.Config;
using cn.lovelong.Paypal.Enums;
using cn.lovelong.Paypal.Model; namespace cn.lovelong.Paypal.Paypal
{
/// <summary>
/// CreatePayment 的摘要说明
/// </summary>
public class CreatePayment
{
public CreatePayment()
{
} public PaymentResult Pay(string json)
{
var jsonResult = HttpHelper.PostJson(
UrlConfig.CreatePaymentUrl,
AccountConfig.ClientId, AccountConfig.Secret, json,
Encoding.UTF8);
var result = new JavaScriptSerializer().Deserialize<PaymentResult>(jsonResult);
return result;
} public PaymentResult Pay(PaymentParam param)
{
var json = GetPayParams(param);
return Pay(json);
} public string GetPayParams(PaymentParam param)
{
var total = param.Total.ToString("N");
var currency = Enum.GetName(typeof (PaypalCurrency), param.Currency);
var payParams = new
{
intent = "sale",
redirect_urls = new
{
return_url = param.ReturnUrl,
cancel_url = param.CancelUrl,
},
payer = new
{
payment_method = "paypal"
},
transactions = new dynamic[]
{
new
{
amount = new
{
total = total,
currency = currency
},
description = param.Description,
custom = param.Code,
item_list = new
{
items = new dynamic[]
{
new
{
name = param.Name,
//description = param.Name,
quantity = "",
price = total,
//tax = "0.01",
//sku = "1",
currency = currency
}
}
}
}
}
};
var json = new JavaScriptSerializer().Serialize(payParams);
return json;
} public string GetFullPayParams(decimal total, PaypalCurrency currency, string returnUrl, string cancelUrl)
{
var payParams = new
{
intent = "sale",
redirect_urls = new
{
return_url = returnUrl,
cancel_url = cancelUrl,
},
payer = new
{
payment_method = "paypal"
},
transactions = new dynamic[]
{
new
{
amount = new
{
total = total.ToString("N"),
currency = Enum.GetName(typeof(PaypalCurrency),currency),
details = new
{
subtotal = "30.00",
tax = "0.07",
shipping = "0.03",
handling_fee = "1.00",
shipping_discount = "-1.00",
insurance = "0.01"
}
},
description = "",
custom = "EBAY_EMS_90048630024435",
invoice_number = "",
payment_options = new
{
allowed_payment_method = "INSTANT_FUNDING_SOURCE"
},
soft_descriptor = "ECHI5786786",
item_list = new
{
items = new dynamic[]
{
new
{
name = "hat",
description = "Brown hat.",
quantity = "",
price = "",
tax = "0.01",
sku = "",
currency = "USD"
}
},
shipping_address = new
{
recipient_name = "Brian Robinson",
line1 = "4th Floor",
line2 = "Unit #34",
city = "San Jose",
country_code = "US",
postal_code = "",
phone = "",
state = "CA"
},
}
}
}
};
var json = new JavaScriptSerializer().Serialize(payParams);
return json;
}
}
}
确认支付接口:
using System.Text;
using System.Web.Script.Serialization;
using cn.lovelong.Paypal.Config;
using cn.lovelong.Paypal.Model; namespace cn.lovelong.Paypal.Paypal
{
/// <summary>
/// Approved 的摘要说明
/// </summary>
public class Approved
{
public PaymentResult DoJson(string paymentId, dynamic json)
{
var jsonResult = HttpHelper.PostJson(string.Format(UrlConfig.ApprovedUrl, paymentId),
AccountConfig.ClientId, AccountConfig.Secret, json, Encoding.UTF8);
var result = new JavaScriptSerializer().Deserialize<PaymentResult>(jsonResult);
return result;
} public PaymentResult Do(string paymentId, string payerId)
{
var json = GetPayParams(payerId);
return DoJson(paymentId, json);
} public string GetPayParams(string payerId)
{
var payParams = new
{
payer_id = payerId
};
var json = new JavaScriptSerializer().Serialize(payParams);
return json;
}
}
}
查询支付结果接口调用:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web.Script.Serialization;
using cn.lovelong.Paypal.Config;
using cn.lovelong.Paypal.Model; namespace cn.lovelong.Paypal.Paypal
{
public class ShowPaymentDetails
{
public PaymentResult Do(string paymentId)
{
var json = HttpHelper.Get(
string.Format(UrlConfig.ShowPaymentDetailsUrl, paymentId),
AccountConfig.ClientId, AccountConfig.Secret,
Encoding.UTF8);
var result = new JavaScriptSerializer().Deserialize<PaymentResult>(json);
return result;
}
}
}
最容易出问题的反而是通用类 HttpHelper:
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Security.Policy;
using System.Text;
using System.Threading.Tasks; namespace cn.lovelong.Paypal
{
public class HttpHelper
{
public static string PostForm(string url, string userName, string password, Dictionary<string,object> dic, Encoding encoding)
{
var param = string.Empty;
foreach (var o in dic)
{
if (string.IsNullOrEmpty(param))
param += o.Key + "=" + o.Value;
else
param += "&" + o.Key + "=" + o.Value;
}
byte[] byteArray = encoding.GetBytes(param); //处理HttpWebRequest访问https有安全证书的问题( 请求被中止: 未能创建 SSL/TLS 安全通道。)
ServicePointManager.ServerCertificateValidationCallback += (s, cert, chain, sslPolicyErrors) => true;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls; HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.Headers.Add("Authorization", "Basic " + Convert.ToBase64String(encoding.GetBytes(userName + ":" + password)));
request.PreAuthenticate = true; request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
request.ContentLength = byteArray.Length; //写入参数
Stream newStream = request.GetRequestStream();
newStream.Write(byteArray, 0, byteArray.Length);
newStream.Close(); using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
using (var stream = response.GetResponseStream())
{
if(stream != null)
using (StreamReader sr = new StreamReader(stream, Encoding.UTF8))
{
return sr.ReadToEnd();
}
}
}
return string.Empty;
} public static string PostJson(string url, string userName, string password, string json, Encoding encoding)
{
byte[] byteArray = encoding.GetBytes(json); //处理HttpWebRequest访问https有安全证书的问题( 请求被中止: 未能创建 SSL/TLS 安全通道。)
ServicePointManager.ServerCertificateValidationCallback += (s, cert, chain, sslPolicyErrors) => true;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 |
SecurityProtocolType.Tls; HttpWebRequest request = (HttpWebRequest) WebRequest.Create(url);
request.Headers.Add("Authorization", "Basic " + Convert.ToBase64String(encoding.GetBytes(userName + ":" + password)));
request.PreAuthenticate = true; request.Method = "POST";
request.Headers.Add("Cache-Control", "no-cache");
request.ContentType = "application/json";
request.ContentLength = byteArray.Length; //写入参数
Stream newStream = request.GetRequestStream();
newStream.Write(byteArray, 0, byteArray.Length);
newStream.Close(); using (HttpWebResponse response = (HttpWebResponse) request.GetResponse())
{
using (var stream = response.GetResponseStream())
{
if (stream != null)
using (StreamReader sr = new StreamReader(stream, Encoding.UTF8))
{
return sr.ReadToEnd();
}
}
}
return string.Empty;
} public static string Get(string url, string userName, string password, Encoding encoding)
{
//处理HttpWebRequest访问https有安全证书的问题( 请求被中止: 未能创建 SSL/TLS 安全通道。)
ServicePointManager.ServerCertificateValidationCallback += (s, cert, chain, sslPolicyErrors) => true;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 |
SecurityProtocolType.Tls; HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.Headers.Add("Authorization", "Basic " + Convert.ToBase64String(encoding.GetBytes(userName + ":" + password)));
request.PreAuthenticate = true; request.Method = "GET";
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
using (var stream = response.GetResponseStream())
{
if (stream != null)
using (StreamReader sr = new StreamReader(stream, Encoding.UTF8))
{
return sr.ReadToEnd();
}
}
}
return string.Empty;
}
}
}
主要的功能都已经实现了!看看演示 Demo 吧!
1. 支付页面
2. Checkout JS 方式(如果你的页面点击登录之后一直在第二个页面转圈的话,那只能说明你的登录账号不能支付你的商家账号,或者你的账号如果登录之后显示添加银行卡的提示,说明你的商家账号和你的账号都是中国账号,那你只能添加多币种卡支付,不能用银联支付了):
付款就好了!
3. 接口方式我就没有使用弹出页面了,最简单的方式(接口会直接在调用接口的页面触发支付跳转),点击接口支付
我就不支付了,我用的商家账号是自己的新加坡的账号, 按照今天的汇率 $0.01 = ¥0.068,你至少需要支付 0.07 元才能完成支付,而文章开头也说了,商家需要付税,也就是说你支付的 0.07 都会变成给 Paypal 的税,商家一分钱也拿不到,也就是说,你至少支付 3.5元人民币($0.51 = ¥3.481)商家才能得到微额的款项。
下面给出 Demo 源码,源码中配置的商家号是我自己的,请自行修改,为了方便大家没有商家账号的朋友做测试我就不删除了,朋友们也不要真的支付测试,你的测试只会让 Paypal 赚钱而已!
我的开发环境是 VS2015 + C# 6.0 + JS ,代码仅供参考,请自行修改扩展学习使用! 也可以来我自己的网站坐坐:https://blog.lovelong.cn/
Paypal 支付功能的 C# .NET / JS 实现的更多相关文章
- java实现网站paypal支付功能并且异步修改订单的状态
java实现网站paypal支付功能并且异步修改订单的状态:步骤如下 第一步:去paypal的官网https://www.paypal.com注册一个个人账号,在创建沙箱测试账号时需要用到 第二步:p ...
- 微信小程序在线支付功能使用总结
最近需要在微信小程序中用到在线支付功能,于是看了一下官方的文档,发现要在小程序里实现微信支付还是很方便的,如果你以前开发过服务号下的微信支付,那么你会发现其实小程序里的微信支付和服务号里的开发过程如出 ...
- C#开发微信门户及应用(40)--使用微信JSAPI实现微信支付功能
在我前面的几篇博客,有介绍了微信支付.微信红包.企业付款等各种和支付相关的操作,不过上面都是基于微信普通API的封装,本篇随笔继续微信支付这一主题,继续介绍基于微信网页JSAPI的方式发起的微信支付功 ...
- Paypal支付小记
Paypal支付小记 *:first-child { margin-top: 0 !important; } body>*:last-child { margin-bottom: 0 !impo ...
- Android下集成Paypal支付
近期项目需要研究paypal支付,官网上的指导写的过于复杂,可能是老外的思维和中国人不一样吧.难得是发现下面这篇文章: http://www.androidhive.info/2015/02/andr ...
- ecshop增加pc扫描二维码微信支付功能代码
ecshop开发网站,如果没有手机版,又想通过微信支付,可以加入pc二维码扫描微信支付功能 工具/原料 ecshop商城系统,phpqrcode,WxPayPubHelper 公众号已申请微信支付 方 ...
- 调用支付宝PHP接口API实现在线即时支付功能(UTF-8编码)
这次在项目中要实现订单功能,所以要完成在线支付,在线支付一般有网银支付和第三方支付(支付宝.paypal等)这两种途径,未简单起见,先完成支付宝在线支付功能,由于项目基于Yii框架,且使用UTF-8编 ...
- [5] 微信公众号开发 - 微信支付功能开发(网页JSAPI调用)
1.微信支付的流程 如下三张手机截图,我们在微信网页端看到的支付,表面上看到的是 "点击支付按钮 - 弹出支付框 - 支付成功后出现提示页面",实际上的核心处理过程是: 点击支付按 ...
- 让你的微信小程序具有在线支付功能
前言 最近需要在微信小程序中用到在线支付功能,于是看了一下官方的文档,发现要在小程序里实现微信支付还是很方便的,如果你以前开发过服务号下的微信支付,那么你会发现其实小程序里的微信支付和服务号里的开发过 ...
随机推荐
- css实用属性
background-size: 100% 100%; 背景通过拉伸实现填充 自适应 overflow: hidden; ...
- [LeetCode] Minimum Cost to Merge Stones 混合石子的最小花费
There are N piles of stones arranged in a row. The i-th pile has stones[i] stones. A move consists ...
- PHP 反射的简单使用
反射机制简介 之前已经介绍过Java反射机制的简单使用,所有的反射机制的思想作用等都是类似的,下面就一起来了解一下PHP反射机制. 个人理解:反射机制就是可以利用类名或者一个类的对象来获取关于这个类的 ...
- Centos 搭建邮箱系统
总结 我实操的过程,2个邮箱都没有界面,都只是邮件系统.可能还需要再部署其他东西,暂止. sendmail 比较简单,主要是发邮件,使用 stmp.还需要解决收邮件的问题和管理界面的问题. postf ...
- thinkphp 单图上传组建成数组然后追加到一个字段
//上传的数组字段 $note1 = input('note1'); $note2 = input('note2'); $note3 = input('note3'); $note4 = input( ...
- pymongo的操作
实例化和插入 from pymongo import MongoClient class TestMongo: def __init__(self): client = MongoClient(hos ...
- javascript基础(Array)
1,join() Array.join(),不改变原数组,将数组中所有元素转换为字符串并连接在一起,返回最后生成的字符串 let a=[1,2,3]; a.join(); // =>" ...
- 解决 spring-cloud-starter-zipkin 启动错误
应用场景:Spring Boot 服务添加 Zipkin 依赖,进行服务调用的数据采集,然后进行 Zipkin-Server 服务调用追踪显示. 示例pom.xml配置: <parent> ...
- 清除SqlServer日志
--在SQL2008中清除日志就必须在简单模式下进行,等清除动作完毕再调回到完全模式. USE [master]GO --GPSLocus是要清除日志的数据库名称ALTER DATABASE [DbN ...
- NeuChar 平台使用及开发教程(二):设置平台账号
在上一篇<NeuChar 平台使用及开发教程(一):开始使用 NeuChar>中我们了解了 NeuChar 的角色和大体功能框架,并进行了注册,本文将介绍如何设置多账号,以便让 NeuCh ...