如何在 NativeScript Vue 中设置“nativescript-stripe”
How to setup ‘nativescript-stripe’ in NativeScript Vue
我正在尝试在我的 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) 相同的代码,只是我设置了 publishableKey 和 backendBaseURL,并导出了一个 class 对于 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 class 也移到它自己的文件中,但不要相信这就是现在的问题。
应用程序崩溃并显示错误消息:
CONSOLE ERROR file:///node_modules/nativescript-vue/dist/index.js:2129:21
[Vue warn]: Error in mounted hook:
"TypeError: _shared_stripe_stripe_service__WEBPACK_IMPORTED_MODULE_6__["StripeService"].createPaymentSession is not a function.
(In '_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__["StripeService"].createPaymentSession' is undefined)"
编辑:
我终于能够 运行 使用此设置的应用程序:
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 width="32" height="20" :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();
}
}
我现在面临的问题(也是设置的一部分)是当我点击 "Payment type" 时没有任何反应。当我调试时,我可以看到它进入方法 presentPaymentMethods()。插件中的这段代码 运行ning 没有任何错误,但没有任何反应:
StripePaymentSession.prototype.presentPaymentMethods = function () {
this.ensureHostViewController();
this.native.presentPaymentOptionsViewController();
};
有人吗?
我花了好几个小时终于弄明白了。感谢@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 width="32" height="20" :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»
插件。跟随插件 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) 相同的代码,只是我设置了 publishableKey 和 backendBaseURL,并导出了一个 class 对于 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 class 也移到它自己的文件中,但不要相信这就是现在的问题。
应用程序崩溃并显示错误消息:
CONSOLE ERROR file:///node_modules/nativescript-vue/dist/index.js:2129:21 [Vue warn]: Error in mounted hook: "TypeError: _shared_stripe_stripe_service__WEBPACK_IMPORTED_MODULE_6__["StripeService"].createPaymentSession is not a function. (In '_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__["StripeService"].createPaymentSession' is undefined)"
编辑:
我终于能够 运行 使用此设置的应用程序:
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 width="32" height="20" :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();
}
}
我现在面临的问题(也是设置的一部分)是当我点击 "Payment type" 时没有任何反应。当我调试时,我可以看到它进入方法 presentPaymentMethods()。插件中的这段代码 运行ning 没有任何错误,但没有任何反应:
StripePaymentSession.prototype.presentPaymentMethods = function () {
this.ensureHostViewController();
this.native.presentPaymentOptionsViewController();
};
有人吗?
我花了好几个小时终于弄明白了。感谢@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 width="32" height="20" :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