我正在尝试创建一个Angular Stripe组件following this tutorial。它说要创建typings.d.ts
(在目录directoy中创建),其内容如下:
declare var stripe: any;
declare var elements: any;
已完成,stripe
中的脚本元素包含了index.html
,但是在尝试编译时,我仍然收到以下消息:
ERROR in src/app/stripe/stripe.component.ts:26:17 - error TS2552: Cannot find name 'elements'. Did you mean 'Element'?
26 this.card = elements.create('card');
~~~~~~~~
node_modules/typescript/lib/lib.dom.d.ts:5063:13
5063 declare var Element: {
~~~~~~~
'Element' is declared here.
src/app/stripe/stripe.component.ts:47:36 - error TS2304: Cannot find name 'stripe'.
47 const { token, error } = await stripe.createToken(this.card);
VSCode没有绘制任何红线。
有想法吗?
import {
Component,
AfterViewInit,
OnDestroy,
ViewChild,
ElementRef,
ChangeDetectorRef
} from '@angular/core';
import { NgForm } from '@angular/forms';
@Component({
selector: 'app-stripe',
templateUrl: './stripe.component.html',
styleUrls: ['./stripe.component.scss']
})
export class StripeComponent implements AfterViewInit, OnDestroy {
@ViewChild('cardInfo', {static: true}) cardInfo: ElementRef;
card: any;
cardHandler = this.onChange.bind(this);
error: string;
constructor(private cd: ChangeDetectorRef) { }
ngAfterViewInit() {
this.card = elements.create('card');
this.card.mount(this.cardInfo.nativeElement);
this.card.addEventListener('change', this.cardHandler);
}
ngOnDestroy() {
this.card.removeEventListener('change', this.cardHandler);
this.card.destroy();
}
onChange({ error }) {
if (error) {
this.error = error.message;
} else {
this.error = null;
}
this.cd.detectChanges();
}
async onSubmit(form: NgForm) {
const { token, error } = await stripe.createToken(this.card);
if (error) {
console.log('Something is wrong:', error);
} else {
console.log('Success!', token);
// ...send the token to the your backend to process the charge
}
}
}
答案 0 :(得分:2)
不太确定您使用的是哪个版本,但最近实现了最新的stripe元素。
加载条带脚本的服务
lazy val core = RootProject(file("../coreProject"))
val main = Project(id = "application", base = file(".")).dependsOn(core)
组件
import { Injectable } from '@angular/core';
interface Scripts {
name: string;
src: string;
}
declare var document: any;
@Injectable({ providedIn: 'root' })
export class DynamicScriptLoaderService {
private scripts: any = {};
constructor() {}
loadScript(src: string) {
return new Promise((resolve, reject) => {
if (!this.scripts[src]) {
this.scripts[src] = true;
//load script
let script = document.createElement('script');
script.type = 'text/javascript';
script.src = src;
if (script.readyState) {
//IE
script.onreadystatechange = () => {
if (script.readyState === 'loaded' || script.readyState === 'complete') {
script.onreadystatechange = null;
resolve({ script: src, loaded: true, status: 'Loaded' });
}
};
} else {
//Others
script.onload = () => {
resolve({ script: name, loaded: true, status: 'Loaded' });
};
}
script.onerror = (error: any) => resolve({ script: name, loaded: false, status: 'Loaded' });
document.getElementsByTagName('head')[0].appendChild(script);
} else {
resolve({ script: name, loaded: true, status: 'Already Loaded' });
}
});
}
}
模板
import { Component, OnInit, ChangeDetectionStrategy, Input, ViewChild, Inject, Output, EventEmitter } from '@angular/core';
import { UserService } from 'shared-components/lib/app-material/user.service';
import { PublicProductService } from '../../public-product.service';
import { DynamicScriptLoaderService } from 'shared-components/lib/app-material/script-loader';
import { BehaviorSubject } from 'rxjs';
import { FormControl, Validators } from '@angular/forms';
import { STRIPE_PUBLIC_KEY } from 'shared-components/lib/common';
declare var Stripe: any;
@Component({
selector: 'flm-stripe',
templateUrl: './stripe.component.html',
styleUrls: ['./stripe.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class StripeComponent implements OnInit {
@Input() order;
stripe: any;
card: any;
sendingRequest$ = new BehaviorSubject(false);
error$ = new BehaviorSubject(null);
nameOnCardControl = new FormControl(null, Validators.required);
@Output() complete = new EventEmitter();
@ViewChild('cardNumber') cardNumber;
@ViewChild('expiry') expiry;
@ViewChild('cvc') cvc;
constructor(
private productService: PublicProductService,
private scriptoader: DynamicScriptLoaderService,
private userservice: UserService,
@Inject(STRIPE_PUBLIC_KEY) private publicKey: string
) {
super();
this.nameOnCardControl.setValue(this.userservice.user.fullName);
}
ngOnInit() {
this.scriptoader.loadScript('https://js.stripe.com/v3/').then(() => {
this.stripe = Stripe(this.publicKey);
var elements = this.stripe.elements();
// Custom styling can be passed to options when creating an Element.
// (Note that this demo uses a wider set of styles than the guide below.)
var style = {
base: {
iconColor: '#666EE8',
color: '#3eb5f1',
lineHeight: '40px',
fontWeight: 300,
fontFamily: 'Roboto, "Helvetica Neue", sans-serif',
fontSize: '16px',
'::placeholder': {
color: '#3eb5f1'
}
},
invalid: {
color: '#e4584c',
iconColor: '#e4584c'
}
};
// Create an instance of the card Element.
this.card = elements.create('cardNumber', { style: style });
// Add an instance of the card Element into the `card-element` <div>.
this.card.mount(this.cardNumber.nativeElement);
elements.create('cardExpiry', { style: style }).mount(this.expiry.nativeElement);
elements.create('cardCvc', { style: style }).mount(this.cvc.nativeElement);
});
}
pay(event) {
if (event) {
event.preventDefault();
}
this.sendingRequest$.next(true);
/// REad this how to create intent https://stripe.com/docs/payments/payment-intents
this.productService.getPaymentData(this.order.Id, 'Stripe').safeSubscribe(this, resp => {
this.stripe
.handleCardPayment(resp.Data, this.card, {
payment_method_data: {
billing_details: {
name: this.nameOnCardControl.value
}
}
})
.then(result => {
this.error$.next(null);
this.sendingRequest$.next(false);
if (result.error) {
this.error$.next(result.error.message);
} else {
this.complete.next(true);
}
});
});
}
}
注意:我正在使用自己的safeSubscribe定制来取消对ngDestroy的订阅,请更改为订阅并自行管理退订。
typings.d.ts-也可以使用,但这取决于您的设置以及根文件夹对您的意义。