如何在 NativeScript Vue 中设置“nativescript-stripe”
Posted
技术标签:
【中文标题】如何在 NativeScript Vue 中设置“nativescript-stripe”【英文标题】:How to setup ‘nativescript-stripe’ in NativeScript Vue 【发布时间】:2020-01-04 05:03:44 【问题描述】:我正在尝试在我的 Nativescript Vue
应用程序中设置 «nativescript-stripe»
插件。跟随插件 github 上的演示有点困难,因为他们只有 Angular and TypeScript
的演示。有没有人让StandardComponent
与 Vue 一起工作,并且可以告诉我在哪里以及将哪些参数发送到StripeService.createPaymentSession()
?
我尝试在模板中设置<Page ref=«cartPage»>
并在mounted()
设置paymentSession:
import
StripeAddress,
StripePaymentData,
StripePaymentListener,
StripePaymentMethod,
StripePaymentSession,
StripeShippingMethod,
StripeShippingMethods
from "nativescript-stripe/standard";
import StripeService, Listener from "~/shared/stripe/stripe.service";
var paymentSession = ;
export default
mounted()
//not sure if this is the way to do it
paymentSession = StripeService.createPaymentSession(
this.$refs.cartPage,
this.stripeItem.price,
new Listener(this)
);
,
在我的 stripe.service.ts 文件中,我得到了与 Angular 演示 (https://github.com/triniwiz/nativescript-stripe/blob/master/demo-angular/app/demo/stripe.service.ts) 相同的代码,除了我设置了可发布密钥和 backendBaseURL,并且还为 Listener 导出了一个类:
export class Listener
public component;
constructor(component)
this.component = component;
onCommunicatingStateChanged(_isCommunicating)
this.component.changeDetectionRef.detectChanges();
//etc. (code from https://github.com/triniwiz/nativescript-stripe/blob/master/demo-angular/app/demo/standard.component.ts)
我想也许我也应该将 Listener 类移到它自己的文件中,但不要相信这是现在的问题。
应用程序崩溃并显示错误消息:
控制台错误文件:///node_modules/nativescript-vue/dist/index.js:2129:21 [Vue 警告]:挂载钩子错误: "TypeError: _shared_stripe_stripe_service__WEBPACK_IMPORTED_MODULE_6__["StripeService"].createPaymentSession 不是函数。 (在 '_shared_stripe_stripe_service__WEBPACK_IMPORTED_MODULE_6__["StripeService"].createPaymentSession(this.$refs.cartPage, this.stripeItem.price, new _shared_stripe_stripe_service__WEBPACK_IMPORTED_MODULE_6__"Listener")', '_shared_stripe_stripe_service__WEBPACK_IMPORTED_MODULE_6__[" >
编辑:
我终于能够使用此设置运行应用程序:
ShoppingCart.vue:
<template>
<Page ref="cartPage" class="page">
<ActionBar class="action-bar">
<NavigationButton ios:visibility="collapsed" icon="res://menu" @tap="onDrawerButtonTap"></NavigationButton>
<ActionItem
icon="res://navigation/menu"
android:visibility="collapsed"
@tap="onDrawerButtonTap"
ios.position="left"
></ActionItem>
<Label class="action-bar-title" text="ShoppingCart"></Label>
</ActionBar>
<StackLayout class="page p-10">
<GridLayout rows="auto" columns="auto,*">
<Label row="0" col="0" :text="stripeItem.name" class="h2"></Label>
<Label
row="0"
col="1"
:text="'kr' + stripeItem.price"
class="text-right text-muted"
></Label>
</GridLayout>
<StackLayout class="hr-light m-10"></StackLayout>
<GridLayout
rows="auto"
columns="*,auto"
@tap="showPaymentMethods()"
class="list-group-item"
>
<Label row="0" col="0" text="Payment Type"></Label>
<StackLayout row="0" col="1" orientation="horizontal">
<Image :src="paymentImage"></Image>
<Label
:text="paymentType"
class="text-right text-muted"
:visibility="!isLoading ? 'visible' : 'collapse'"
></Label>
</StackLayout>
<ActivityIndicator
row="0"
col="1"
:busy="isLoading"
:visibility="isLoading ? 'visible' : 'collapse'"
></ActivityIndicator>
</GridLayout>
<StackLayout class="hr-light m-10"></StackLayout>
<GridLayout rows="auto" columns="auto,*" @tap="showShipping()" class="list-group-item">
<Label row="0" col="0" text="Shipping Method"></Label>
<Label row="0" col="1" :text="shippingType" class="text-right text-muted"></Label>
</GridLayout>
<StackLayout class="hr-light m-10"></StackLayout>
<GridLayout rows="auto" columns="auto,*" class="list-group-item">
<Label row="0" col="0" text="Total"></Label>
<Label row="0" col="1" :text="'kr ' + total" class="text-right"></Label>
</GridLayout>
<StackLayout class="hr-light m-10"></StackLayout>
<Label :text="errorMessage" class="text-danger" textWrap="true"></Label>
<Button text="Buy" :isEnabled="canBuy" class="btn btn-primary btn-active" @tap="buy()"></Button>
<ActivityIndicator
:busy="paymentInProgress"
:visibility="paymentInProgress ? 'visible' : 'collapse'"
></ActivityIndicator>
<Label :text="successMessage" class="text-primary" textWrap="true"></Label>
<StackLayout class="hr-light m-10"></StackLayout>
<Label text="Debug Info"></Label>
<Label :text="debugInfo" class="body" textWrap="true"></Label>
</StackLayout>
</Page>
</template>
<script>
import * as utils from "~/shared/utils";
import SelectedPageService from "../shared/selected-page-service";
import StripeService, Listener from "~/shared/stripe/stripe.service.ts";
const Page = require("tns-core-modules/ui/page").Page;
let stripeService = new StripeService();
var paymentSession = ;
export default
mounted()
SelectedPageService.getInstance().updateSelectedPage("ShoppingCart");
paymentSession = stripeService.createPaymentSession(new Page(), 1213, new Listener(this));
,
data()
return
stripeItem:
id: 0,
name: "Something to buy",
price: 1200
,
paymentInProgress: false,
canBuy: true,
isLoading: false,
paymentType: "",
paymentImage: "",
shippingType: "",
total: "",
debugInfo: "",
successMessage: "",
errorMessage: ""
;
,
methods:
onDrawerButtonTap()
utils.showDrawer();
,
showPaymentMethods()
return stripeService.showPaymentMethods(paymentSession);
,
showShipping()
return stripeService.showShipping(paymentSession);
,
buy()
this.paymentInProgress = true;
this.canBuy = false;
return stripeService.requestPayment(paymentSession);
;
</script>
stripe.service.ts:
import StripeAddress, StripeBackendAPI, StripeConfig, StripeCustomerSession, StripePaymentListener, StripePaymentSession, StripeShippingAddressField, StripeShippingMethod from "nativescript-stripe/standard";
import * as httpModule from "tns-core-modules/http";
import Page from "tns-core-modules/ui/page";
export const publishableKey = "pk_test_xxxxremovedxxxx";
const backendBaseURL = "https://xxxxremovedxxxx.herokuapp.com/";
const appleMerchantID = "";
export class Listener
public component;
constructor(component)
this.component = component;
onCommunicatingStateChanged(_isCommunicating)
onPaymentDataChanged(data)
this.component.paymentMethod = data.paymentMethod;
this.component.shippingInfo = data.shippingInfo;
this.component.shippingAddress = data.shippingAddress;
onPaymentSuccess()
this.component.successMessage =
`Congratulations! You bought a "$this.component.item.name" for $$this.component.item.price / 100.`;
onUserCancelled()
onError(_errorCode, message)
this.component.errorMessage = message;
provideShippingMethods(address)
let upsGround =
amount: 0,
label: "UPS Ground",
detail: "Arrives in 3-5 days",
identifier: "ups_ground"
;
let upsWorldwide =
amount: 1099,
label: "UPS Worldwide Express",
detail: "Arrives in 1-3 days",
identifier: "ups_worldwide"
;
let fedEx =
amount: 599,
label: "FedEx",
detail: "Arrives tomorrow",
identifier: "fedex"
;
let methods = ;
if (!address.country || address.country === "US")
methods['isValid'] = true;
methods['validationError'] = undefined;
methods['shippingMethods'] = [upsGround, fedEx];
methods['selectedShippingMethod'] = fedEx;
else if (address.country === "AQ")
methods['isValid'] = false;
methods['validationError'] = "We can't ship to this country.";
else
fedEx.amount = 2099;
methods['isValid'] = true;
methods['validationError'] = undefined;
methods['shippingMethods'] = [upsWorldwide, fedEx];
methods['selectedShippingMethod'] = fedEx;
return methods;
export class StripeService implements StripeBackendAPI
private customerSession: StripeCustomerSession;
constructor()
if (-1 !== publishableKey.indexOf("pk_test_yours"))
throw new Error("publishableKey must be changed from placeholder");
if (-1 !== backendBaseURL.indexOf("https://yours.herokuapp.com/"))
throw new Error("backendBaseURL must be changed from placeholder");
StripeConfig.shared().backendAPI = this;
StripeConfig.shared().publishableKey = publishableKey;
StripeConfig.shared().appleMerchantID = appleMerchantID;
StripeConfig.shared().companyName = "Demo Company";
StripeConfig.shared().requiredShippingAddressFields = [StripeShippingAddressField.PostalAddress];
this.customerSession = new StripeCustomerSession();
private backendURL(pathComponent: string): string
if (!backendBaseURL) throw new Error("backendBaseURL must be set");
if (!backendBaseURL.endsWith("/"))
return backendBaseURL + "/" + pathComponent;
else
return backendBaseURL + pathComponent;
createCustomerKey(apiVersion: string): Promise<any>
let url = this.backendURL("ephemeral_keys");
return httpModule.request(
url: url,
method: "POST",
headers: "Content-Type": "application/x-www-form-urlencoded; charset=utf-8" ,
content: "api_version=" + apiVersion
).then(response =>
if (response.statusCode < 200 || response.statusCode >= 300)
throw new Error(response.content.toString());
return response.content.toJSON();
);
completeCharge(stripeID: string, amount: number, shippingMethod: StripeShippingMethod, shippingAddress: StripeAddress): Promise<void>
let url = this.backendURL("capture_payment");
return httpModule.request(
url: url,
method: "POST",
headers: "Content-Type": "application/x-www-form-urlencoded; charset=utf-8" ,
content:
"source=" + stripeID +
"&amount=" + amount +
"&" + this.encodeShipping(shippingMethod, shippingAddress)
).then(response =>
if (response.statusCode < 200 || response.statusCode >= 300)
throw new Error(response.content.toString());
);
private encodeShipping(method: StripeShippingMethod, address: StripeAddress): string
function entry(label: string, value: string): string
return value ? encodeURI(label) + "=" + encodeURI(value) : "";
return entry("shipping[carrier]", method.label) +
entry("&shipping[name]", address.name) +
entry("&shipping[address][line1]", address.line1) +
entry("&shipping[address][line2]", address.line2) +
entry("&shipping[address][city]", address.city) +
entry("&shipping[address][state]", address.state) +
entry("&shipping[address][country]", address.country) +
entry("&shipping[address][postal_code]", address.postalCode) +
entry("&phone", address.phone) +
entry("&email", address.email);
createPaymentSession(page, price, listener?): StripePaymentSession
return new StripePaymentSession(page, this.customerSession, price, "usd", listener);
showPaymentMethods(paymentSession: StripePaymentSession)
paymentSession.presentPaymentMethods();
showShipping(paymentSession: StripePaymentSession)
paymentSession.presentShipping();
requestPayment(paymentSession: StripePaymentSession)
paymentSession.requestPayment();
我现在面临的问题(也是设置的一部分)是当我点击“付款类型”时没有任何反应。当我调试时,我可以看到它进入了方法 presentPaymentMethods()。插件中的这段代码运行没有任何错误,但没有任何反应:
StripePaymentSession.prototype.presentPaymentMethods = function ()
this.ensureHostViewController();
this.native.presentPaymentOptionsViewController();
;
有人吗?
【问题讨论】:
你不能简单的构造new Page()
,它应该是你的组件所在的页面。如果您仍然有,请分享完整的干净代码,包括您在服务中的所有内容并发布错误日志。
我已经用完整的代码@Manoj 更新了帖子
我尝试了“require('ui/frame').topmost().currentPage”而不是“new Page()”。仍然没有错误,视图没有变化@Manoj
使用cartPage
的loaded
事件并将其作为args.object
传递,其中args
应该是加载事件回调的参数。
我照你说的做了,args.object 有一个道具typeName="Page"
所以我怀疑它是正确的对象,但仍然没有。我的页面加载事件现在看起来像这样:onPageLoaded(args) var comp = this; paymentSession = stripeService.createPaymentSession( args.object, comp.stripeItem.price, new Listener(comp) );
【参考方案1】:
花了好几个小时终于弄明白了。感谢@Manoj 的提示。
stripe.service.ts:
import StripeAddress, StripeBackendAPI, StripeConfig, StripeCustomerSession, StripePaymentListener, StripePaymentSession, StripeShippingAddressField, StripeShippingMethod from "nativescript-stripe/standard";
import * as httpModule from "tns-core-modules/http";
// 1) To get started with this demo, first head to https://dashboard.stripe.com/account/apikeys
// and copy your "Test Publishable Key" (it looks like pk_test_abcdef) into the line below.
export const publishableKey = "pk_test_yours";
// 2) Next, optionally, to have this demo save your user's payment details, head to
// https://github.com/stripe/example-ios-backend , click "Deploy to Heroku", and follow
// the instructions (don't worry, it's free). Paste your Heroku URL below
// (it looks like https://blazing-sunrise-1234.herokuapp.com ).
const backendBaseURL = "https://yours.herokuapp.com/";
// 3) Optionally, to enable Apple Pay, follow the instructions at https://stripe.com/docs/apple-pay/apps
// to create an Apple Merchant ID. Paste it below (it looks like merchant.com.yourappname).
const appleMerchantID = "";
export class Listener
public component;
constructor(component)
this.component = component;
onCommunicatingStateChanged(_isCommunicating)
onPaymentDataChanged(data)
this.component.paymentMethod = data.paymentMethod;
this.component.shippingInfo = data.shippingInfo;
this.component.shippingAddress = data.shippingAddress;
onPaymentSuccess()
this.component.successMessage =
`Congratulations! You bought a "$this.component.stripeItem.name" for $$this.component.stripeItem.price / 100.`;
onUserCancelled()
onError(_errorCode, message)
this.component.errorMessage = message;
provideShippingMethods(address)
let upsGround =
amount: 0,
label: "UPS Ground",
detail: "Arrives in 3-5 days",
identifier: "ups_ground"
;
let upsWorldwide =
amount: 1099,
label: "UPS Worldwide Express",
detail: "Arrives in 1-3 days",
identifier: "ups_worldwide"
;
let fedEx =
amount: 599,
label: "FedEx",
detail: "Arrives tomorrow",
identifier: "fedex"
;
let methods = ;
if (!address.country || address.country === "US")
methods['isValid'] = true;
methods['validationError'] = undefined;
methods['shippingMethods'] = [upsGround, fedEx];
methods['selectedShippingMethod'] = fedEx;
else if (address.country === "AQ")
methods['isValid'] = false;
methods['validationError'] = "We can't ship to this country.";
else
fedEx.amount = 2099;
methods['isValid'] = true;
methods['validationError'] = undefined;
methods['shippingMethods'] = [upsWorldwide, fedEx];
methods['selectedShippingMethod'] = fedEx;
return methods;
export class StripeService implements StripeBackendAPI
private customerSession;
constructor()
if (-1 !== publishableKey.indexOf("pk_test_yours"))
throw new Error("publishableKey must be changed from placeholder");
if (-1 !== backendBaseURL.indexOf("https://yours.herokuapp.com/"))
throw new Error("backendBaseURL must be changed from placeholder");
StripeConfig.shared().backendAPI = this;
StripeConfig.shared().publishableKey = publishableKey;
StripeConfig.shared().appleMerchantID = appleMerchantID;
StripeConfig.shared().companyName = "Demo Company";
StripeConfig.shared().requiredShippingAddressFields = [StripeShippingAddressField.PostalAddress];
this.customerSession = new StripeCustomerSession();
private backendURL(pathComponent: string): string
if (!backendBaseURL) throw new Error("backendBaseURL must be set");
if (!backendBaseURL.endsWith("/"))
return backendBaseURL + "/" + pathComponent;
else
return backendBaseURL + pathComponent;
createCustomerKey(apiVersion: string): Promise<any>
let url = this.backendURL("ephemeral_keys");
return httpModule.request(
url: url,
method: "POST",
headers: "Content-Type": "application/x-www-form-urlencoded; charset=utf-8" ,
content: "api_version=" + apiVersion
).then(response =>
if (response.statusCode < 200 || response.statusCode >= 300)
throw new Error(response.content.toString());
return response.content.toJSON();
);
completeCharge(stripeID: string, amount: number, shippingMethod: StripeShippingMethod, shippingAddress: StripeAddress): Promise<void>
let url = this.backendURL("capture_payment");
return httpModule.request(
url: url,
method: "POST",
headers: "Content-Type": "application/x-www-form-urlencoded; charset=utf-8" ,
content:
"source=" + stripeID +
"&amount=" + amount +
"&" + this.encodeShipping(shippingMethod, shippingAddress)
).then(response =>
if (response.statusCode < 200 || response.statusCode >= 300)
throw new Error(response.content.toString());
);
private encodeShipping(method: StripeShippingMethod, address: StripeAddress): string
function entry(label: string, value: string): string
return value ? encodeURI(label) + "=" + encodeURI(value) : "";
return entry("shipping[carrier]", method.label) +
entry("&shipping[name]", address.name) +
entry("&shipping[address][line1]", address.line1) +
entry("&shipping[address][line2]", address.line2) +
entry("&shipping[address][city]", address.city) +
entry("&shipping[address][state]", address.state) +
entry("&shipping[address][country]", address.country) +
entry("&shipping[address][postal_code]", address.postalCode) +
entry("&phone", address.phone) +
entry("&email", address.email);
createPaymentSession(page, price, listener?): StripePaymentSession
return new StripePaymentSession(page, this.customerSession, price, "usd", listener);
showPaymentMethods(paymentSession: StripePaymentSession)
paymentSession.presentPaymentMethods();
showShipping(paymentSession: StripePaymentSession)
paymentSession.presentShipping();
requestPayment(paymentSession: StripePaymentSession)
paymentSession.requestPayment();
Payment.vue:
<template>
<Page @loaded="onPageLoaded" class="page">
<ActionBar class="action-bar">
<Label class="action-bar-title" text="Home"></Label>
</ActionBar>
<StackLayout class="page p-10">
<GridLayout rows="auto" columns="auto,*">
<Label row="0" col="0" :text="stripeItem.name" class="h2"></Label>
<Label row="0" col="1" :text="'$' + stripeItem.price" class="text-right text-muted"></Label>
</GridLayout>
<StackLayout class="hr-light m-10"></StackLayout>
<GridLayout rows="auto" columns="*,auto" @tap="showPaymentMethods()" class="list-group-item">
<Label row="0" col="0" text="Payment Type"></Label>
<StackLayout row="0" col="1" orientation="horizontal">
<Image :src="paymentImage"></Image>
<Label
:text="paymentType"
class="text-right text-muted"
:visibility="!isLoading ? 'visible' : 'collapse'"
></Label>
</StackLayout>
<ActivityIndicator
row="0"
col="1"
:busy="isLoading"
:visibility="isLoading ? 'visible' : 'collapse'"
></ActivityIndicator>
</GridLayout>
<StackLayout class="hr-light m-10"></StackLayout>
<GridLayout rows="auto" columns="auto,*" @tap="showShipping()" class="list-group-item">
<Label row="0" col="0" text="Shipping Method"></Label>
<Label row="0" col="1" :text="shippingType" class="text-right text-muted"></Label>
</GridLayout>
<StackLayout class="hr-light m-10"></StackLayout>
<GridLayout rows="auto" columns="auto,*" class="list-group-item">
<Label row="0" col="0" text="Total"></Label>
<Label row="0" col="1" :text="'$ ' + total" class="text-right"></Label>
</GridLayout>
<StackLayout class="hr-light m-10"></StackLayout>
<Label :text="errorMessage" class="text-danger" textWrap="true"></Label>
<Button text="Buy" :isEnabled="canBuy" class="btn btn-primary btn-active" @tap="buy()"></Button>
<ActivityIndicator
:busy="paymentInProgress"
:visibility="paymentInProgress ? 'visible' : 'collapse'"
></ActivityIndicator>
<Label :text="successMessage" class="text-primary" textWrap="true"></Label>
<StackLayout class="hr-light m-10"></StackLayout>
<Label text="Debug Info"></Label>
<Label :text="debugInfo" class="body" textWrap="true"></Label>
</StackLayout>
</Page>
</template>
<script>
//Change the import of 'stripe.service.ts' to the right path
import StripeService, Listener from "~/services/stripe.service.ts";
let stripeService = new StripeService();
var paymentSession = null;
export default
data()
return
stripeItem:
id: 0,
name: "Something to buy",
price: 1200
,
paymentInProgress: false,
canBuy: true,
isLoading: false,
paymentType: "",
paymentImage: "",
shippingType: "",
total: "",
debugInfo: "",
successMessage: "",
errorMessage: ""
;
,
methods:
onPageLoaded(args)
var comp = this;
paymentSession = stripeService.createPaymentSession(
args.object,
comp.stripeItem.price,
new Listener(comp)
);
,
showPaymentMethods()
return stripeService.showPaymentMethods(paymentSession);
,
showShipping()
return stripeService.showShipping(paymentSession);
,
buy()
this.paymentInProgress = true;
this.canBuy = false;
return stripeService.requestPayment(paymentSession);
;
</script>
并确保您已安装 TypeScript tns install typescript
【讨论】:
以上是关于如何在 NativeScript Vue 中设置“nativescript-stripe”的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Nativescript HtmlView 标签中设置链接颜色?
NativeScript-Vue 中的分组 UITableView
NativeScript - ios如何在键盘出现时调整视图