net-tools / paypal-js
客户端JS库,用于管理简单的电子商务网站,包括购物车和PayPal
Requires
- php: >= 7.3
- net-tools/js-core: ^1.0.0
This package is auto-updated.
Last update: 2024-08-30 01:32:27 UTC
README
客户端JS库,用于管理简单的电子商务网站,包括购物车和PayPal
设置说明
要安装 net-tools/paypal-js 包,只需通过 composer 引入,并在 HEAD
部分插入一个脚本标签
<script src="/path_to_vendor/net-tools/paypal-js/src/js/client.js"></script>
请注意,net-tools/js-core 也需要安装
<script src="/path_to_vendor/net-tools/js-core/src/js-core.js.min"></script>
如何使用(简单过程)
如果不需要处理购物车,并且不需要客户数据,脚本相当简单
var shop = new NTPaypal.Shop('EUR');
shop.expressButtons('product title', 10.50, 'PHYSICAL_GOODS', '#selector_here')
expressButtons
方法期望一个标题、一个金额、一个类别('PHYSICAL_GOODS'、'DIGITAL_GOODS'、'DONATION')和一个选择器来绘制PayPal按钮。可以省略类别参数(null);它返回一个Promise对象,当支付完成时解析(否则,Promise被拒绝)。
下面是一个如何使用Promise的示例。
如何使用(完整过程,具有购物车支持和流畅API)
对于更复杂的情况,例如购买多个产品、处理运费等,我们必须处理其他对象。一种方法是使用程序化API(见下一章),通过在调用 paypalButtons
方法来初始化支付之前创建客户、购物车和订单对象。
另一种创建所需数据的方法是使用提供的流畅API。
设置PayPal脚本
首先,必须在脚本标签中包含PayPal库(将 XXXXXX 替换为您的paypal客户端ID)
<script src="https://www.paypal.com/sdk/js?currency=EUR&client-id=XXXXXX"></script>
使用流畅API
首先,我们创建一个带有所需货币的 Shop 对象;Shop对象作为工厂来构建其他所需对象
var shop = new NTPaypal.Shop('EUR');
然后,我们开始使用流畅API,从 sell
方法开始。它接受一个 Product 对象来表示购买的单个项目。如果购买不同的产品,可能每个产品的数量不同,则 sell
方法可以接受一个 ProductQuantity 对象数组(关联产品数量)。产品对象可以以编程方式创建,或者使用流畅API提供的 Shop.product
方法(具有标题、成本和类别参数)
shop.sell(shop.product('Product Label', 3.45, 'PHYSICAL_GOODS'))
然后,如果需要,我们使用产品流畅方法
shop.sell(
shop.product('Product Label', 3.45, 'PHYSICAL_GOODS')
.setTax(12)
.withDescription('Fantastic product here')
.setSku('EANXXXXXXX')
)
为了设置产品的单位数量,我们使用 Shop.product
返回的产品对象上的流畅方法 setQuantity
。但是,重要的是要注意,setQuantity
不返回产品对象,而是一个新的 ProductQuantity 类对象,从而防止进一步链式调用。换句话说,必须在 setTax
、withDescription
和 setSku
调用之前调用
shop.sell(
shop.product('Product Label', 3.45, 'PHYSICAL_GOODS')
.setTax(12)
.withDescription('Fantastic product here')
.setSku('EANXXXXXXX')
.setQuantity(3)
)
sell
方法返回一个 Sale 对象,提供了几个流畅方法(to
、withShipping
、withDescription
、withCustom_id
),使得可以链式调用以指定客户、运费、描述和客户端ID。
shop.sell(
shop.product('Product Label', 3.45, 'PHYSICAL_GOODS')
.setTax(12)
.withDescription('Fantastic product here')
.setSku('EANXXXXXXX')
)
.withShipping(32)
.withDescription('Order received, thanks')
.withCustom_id('Invoice nABC123')
.to(
shop.customer()
.named('John', 'Doe'),
.living('123, fifth Avenue')
.in('75000', 'PARIS', 'FR')
.withEmail('john.doe@gmail.com')
.withPhone('0601020304', 'MOBILE')
)
在这里,Sale对象已准备好;请注意,所有流畅API调用(除 sell
及其参数外)都是可选的。为了准备PayPal按钮的渲染,我们调用 payButtonsInside
方法(带有DOM树中的目标选择器),它准备一个Payment对象,然后我们调用其 execute
方法来显示按钮。
shop.sell(
shop.product('Product Label', 3.45, 'PHYSICAL_GOODS')
.setTax(12)
.withDescription('Fantastic product here')
.setSku('EANXXXXXXX')
)
.withShipping(32)
.withDescription('Order received, thanks')
.to(
shop.customer()
.named('John', 'Doe'),
.living('123, fifth Avenue')
.in('75000', 'PARIS', 'FR')
.withEmail('john.doe@gmail.com')
.withPhone('0601020304', 'MOBILE')
)
.payButtonsInside('#div_paypal_here')
.execute();
如果需要一些PayPal应用程序上下文参数,它们可以通过定义 set
和 withApplicationContext
方法的Payment对象来设置
shop.sell(
shop.product('Product Label', 3.45, 'PHYSICAL_GOODS')
.setTax(12)
.withDescription('Fantastic product here')
.setSku('EANXXXXXXX')
)
.withShipping(32)
.withDescription('Order received, thanks')
.to(
shop.customer()
.named('John', 'Doe'),
.living('123, fifth Avenue')
.in('75000', 'PARIS', 'FR')
.withEmail('john.doe@gmail.com')
.withPhone('0601020304', 'MOBILE')
)
.payButtonsInside('#div_paypal_here')
.set('shipping_preference', 'NO_SHIPPING')
.execute();
sell
方法返回一个Promise,当支付完成时解析。
如何使用(完整流程,包含购物车支持,程序化API)
此库定义了几个JavaScript对象来管理产品、客户详情、购物车、订单,并使启动PayPal支付窗口成为可能。
设置PayPal脚本
首先,必须在脚本标签中包含PayPal库(将 XXXXXX 替换为您的paypal客户端ID)
<script src="https://www.paypal.com/sdk/js?currency=EUR&client-id=XXXXXX"></script>
如果需要,可以将PayPal库的加载推迟到显示PayPal "购买"按钮(见下文)。
创建对象
首先,我们创建一个带有所需货币的 Shop 对象;Shop对象作为工厂来构建其他所需对象
var shop = new NTPaypal.Shop('EUR');
然后我们创建客户和产品对象。请注意,必填值作为常规参数传递给构造函数,而非必填项通过一个other
对象字面量参数传递。
var cust = shop.newCustomer('John', {
surname : 'doe',
address1 : '1 Kensington Avenue',
address2 : 'Building B',
zipcode : '75000',
city : 'PARIS',
countrycode : 'FR'
email : 'john.doe@gmail.com',
phone : '0601020304',
phone_type : 'MOBILE'
});
var p1 = shop.newProduct('Product 1', 12.50, 'PHYSICAL_GOODS', {
sku : 'EAN123456',
tax : 1.12,
description : 'Great product 1 here'
});
我们创建一个购物车,里面装满了商品(在这里,一个包含5个单位的商品)和适当的数量。
var cart = shop.newCart([shop.newProductQuantity(p1, 5)]);
最后,我们创建一个订单,将这些对象绑定在一起
var order = shop.newOrder(cart, cust);
到目前为止,还没有显示PayPal按钮。我们仍然可以使用相关的购物车对象方法(add
、remove
、setQuantity
、contains
、count
、empty
)来更新购物车。
然后,要显示PayPal "立即购买"按钮,我们调用Shop对象中的适当函数
shop.paypalButtons(order, '#paypal_buttons_container');
第二个参数是一个选择器,用于标识将包含PayPal按钮的DOM标签(通常是一个DIV标签);将#paypal_buttons_container
替换为任何相关的选择器。在方法调用完成后,PayPal按钮将在容器中渲染,用户可以开始支付。
还有一个第三个参数,这使得可以传递application_context
参数到PayPal API。主要用途是将它设置为{ shipping_preference : 'NO_SHIPPING' }
,从而移除一些不必要的客户地址文本字段(仅在点击“通过卡支付”黑色按钮时)。
数据捕获
上面的JavaScript行使客户端可以进行支付,但我们还没有回调来捕获任何相关数据(如交易ID)或发送“感谢”电子邮件。
paypalButton返回一个Promise,当解析时,将传递有关交易的数据
shop.paypalButtons(order, '#paypal_buttons_container').then(
function(data){
alert('Transaction is OK, with ID ' + data.purchase_units[0].payments.captures[0].id);
}
);
保存/恢复购物车和购物车项目到浏览器存储
由于用户可能浏览多个页面来创建订单,将多个产品放入购物车,因此需要在页面加载、重新加载等之间保存当前购物车内容。
NTPaypal客户端库提供了3种存储策略:cookies(浏览器关闭时购物车内容丢失)、localStorage(购物车内容永远不会丢失)、sessionStorage(页面关闭时购物车内容丢失,但浏览器没有关闭)。
要将购物车内容保存到浏览器存储,必须创建一个具有适当参数的Session对象;然后调用save
方法。要恢复数据,应调用restore
方法。
// creating session
// save/restore will be done through CookiesStorage interface (please note that you mustn't create a CookiesStorage object with new keyword, just pass the constructor reference)
// create new Session object, with storage interface constructor, shop/cart/store objects
var s = new NTPaypal.Session(NTPaypal.CookiesStorage, shop, cart);
// saving a previously created Cart object
s.save();
// ...... some time later
var cart = s.restore();
管理库存和购物车
加载脚本
与商店相关的函数在一个单独的javascript文件中
<script src="/path_to_vendor/net-tools/paypal-js/src/js/store.js"></script>
创建商店
对于简单的支付流程,像上面示例中那样从头创建购物车就足够了。然而,当创建在线商店时,处理产品库存是强制性的;它防止用户将比商店中可用的更多单位的商品放入购物车。
创建一个包含产品和其数量的商店对象,与创建购物车非常相似
// creating 2 products with respectively 20 and 10 units
var p1 = new NTPaypal.ProductQuantity(new NTPaypal.Product('store_product_1', 11.11, 'PHYSICAL_GOODS', 'EUR', { sku : 'prd_1' }), 20);
var p2 = new NTPaypal.ProductQuantity(new NTPaypal.Product('store_product_2', 22.22, 'PHYSICAL_GOODS', 'EUR', { sku : 'prd_2' }), 10);
var store = new NTPaypal.Store([p1, p2]);
由于每种产品的数量可能来自服务器端的数据库,因此可以使用Json数据创建javascript商店对象
var store = NTPaypal.Store.fromJson('{
"inventory": {
"items": [
{
"product": {
"title": "store_product_1",
"price": 11.11,
"category": "PHYSICAL_GOODS",
"tax": 0,
"withDescription": "",
"sku": "prd_1",
"currency_code": "EUR"
},
"quantity": 20
},
{
"product": {
"title": "store_product_2",
"price": 22.22,
"category": "PHYSICAL_GOODS",
"tax": 0,
"withDescription": "",
"sku": "prd_2",
"currency_code": "EUR"
},
"quantity": 10
}
]
}
}');
将商品添加到购物车
要将商品从商店对象移动到之前创建的购物车对象中
store.addToCart('prd_1', cart);
如果给定的SKU产品不存在,则抛出一个错误异常。如果没有更多单位可用在商店中,我们不能将其移动到购物车中,方法addToCart
返回false。
将购物车中的商品移回商店
要将购物车中某种产品的所有单位移除并放回商店
store.removeFromCart('prd_1', cart);
处理购物车中的数量
要更新购物车中产品的数量,有两种方法:一种用于设置数量,另一种用于从当前购物车中的数量添加/移除单位。
// set the amount to 5
store.setCartQuantity('prd_1', 5, cart);
// set the amount to the current amount in the cart minus 1
store.updateCartQuantity('prd_2', -1, cart);
使用库存管理从存储中加载购物车
如果管理库存数量,则在从存储中加载购物车时必须更新商店,以确保商店中的可用数量与总数量减去购物车中的数量相匹配。
为此,必须使用带有商店对象参数的会话对象,并将实现处理特定情况回调的SessionStoreInterface对象传递给恢复方法。
// creating session
var s = new NTPaypal.Session(NTPaypal.CookiesStorage, shop, cart, store);
// ...... some time later
var cart = s.restore(new NTPaypal.SessionStoreInterface(
function(product)
{
// deal with lower quantity available issues
},
function(product)
{
// deal with product unavailable issues
}
));
使用UI
基础知识
提供了一个简单的JavaScript购物车/配送/确认UI。在3个连续的屏幕中,它可以
- 显示购物车内容(增加/减少购物车中项目的数量)
- 询问配送详情
- 提示用户选择承运人(非强制),要求销售条款协议并显示PayPal按钮
默认UI语言为英语,但可以轻松实现翻译(请参阅提供的i18n-fr.js
文件中的法语翻译)。
尽管UI脚本相当大(在单独的ui.js
文件中),但使用相对简单,只需要少量强制参数。
var ui = new NTPaypal.UI("#container", session, {
// get a list of countries available for user shipping ; returns an array of object litterals (country, code, isDefault)
onCountries : function(cart, customer){
return [ {country:'USA', code:'US', isDefault:true}, {country:'United Kingdom', code:'GB'} ];
},
// do some stuff when payment done
onPaymentReceived : function(cart, customer, paypalData){
alert("Payment done with paypal transaction id " + paypalData.purchase_units[0].payments.captures[0].id);
},
// do some stuff after 'payment done' called, during process cleaning
onPaymentCompleted : function()
{
alert('Thanks you, now redirecting you to home page');
document.location = '/';
},
// return a float with shipping cost for cart/customer
onShippingCost : function(cart, customer){
return 12.32;
},
// return a custom message displayed above buttons
onCustomMessage : function(cart, customer){
return "This is a custom message";
},
// display a link to Terms of Sales (not mandatory)
TOSLink : { url : 'https://XXXXXXX', title:'Terms Of Sales : click here' }
});
ui.show();
高级使用:询问用户选择的承运人
在上面的示例中,onShippingCost
回调返回一个带有定义好的配送费用的浮点数(可能是0);我们可以要求用户选择其承运人。承运人由名称、成本、配送时间、图片定义;最后两个参数不是强制性的。
// returning an array of carriers, ie object litterals (carrier, cost, shipping_time, image)
onShippingCost : function(cart, customer){
return [ {carrier:"DHL", cost:24.50, shipping_time:"2 days"}, {carrier:"UPS", cost:20.89, shipping_time:"1 day"} ];
},
使用上述代码,用户将通过列表框选择承运人。
如果用户更喜欢,可以通过点击与承运人图标链接的单选按钮来选择承运人。
// returning an array of carriers, ie object litterals (carrier, cost, shipping_time, image)
onShippingCost : function(cart, customer){
return [ {carrier:"DHL", cost:24.50, image:"url"}, {carrier:"UPS", cost:20.89, image:"data:image/png;base64,xxxxxxx"} ];
},
要使用单选按钮和图片替换列表框,我们添加一个image
属性(可以链接到URL或可以是base64编码的数据字符串)。
在carriers.json
文件中提供了一些base64编码的图片;建议将所需的图片复制/粘贴到您的代码中,没有必要包含包含多个承运人图标的庞大文件,而您可能只需要一个或两个图标。
高级使用:实现异步onPaymentReceived处理器
在上面的示例中,调用、执行onPaymentReceived
处理器,并在其完成后,控制权返回到ui.js脚本,该脚本将进行购物车和会话清理。
如果在onPaymentReceived
中必须在客户端执行某些异步任务,在完成此异步任务之前清理购物车和会话可能是不正确的。
在这种情况下,onPaymentReceived
必须返回一个Promise,当ui.js可以在清理时链接(换句话说,当客户端任务完成时)时将解决。例如,如果在onPaymentReceived
中使用Fetch API调用某些服务器端脚本,我们可以在链接fetch()时将Promise构造函数的resolve
方法的then
参数。
onPaymentReceived : function(cart, customer, paypal){
return new Promise(function(resolve, reject){
console.log(paypal); alert('Payment done, calling server-side script with Fetch API !');
fetch('/path/to/script.php', {paypal_id : paypal.purchase_units[0].payments.captures[0].id})
.then(resolve)
.catch(reject);
});
},