diff --git a/app/controller/stripe.js b/app/controller/stripe.js new file mode 100644 index 0000000000000000000000000000000000000000..f9de02542d84a45dd011b3ae3f45f6323bc25165 --- /dev/null +++ b/app/controller/stripe.js @@ -0,0 +1,16 @@ +'use strict'; + +const Controller = require('egg').Controller; + +class WebhookController extends Controller { + + async webhook() { + // 接收阿里服务器POST提交的XML数据 + const params = this.ctx.request.body; + const result = await this.service.stripe.webhook(params); + console.log('-------------------------------------------------------webhook'); + } + +} + +module.exports = WebhookController; diff --git a/app/graphql/input/schema.graphql b/app/graphql/input/schema.graphql new file mode 100644 index 0000000000000000000000000000000000000000..f6db29d79df2568637cea5c74f4a2a5ddfff74b1 --- /dev/null +++ b/app/graphql/input/schema.graphql @@ -0,0 +1,3 @@ +input Items{ + id:String, +} \ No newline at end of file diff --git a/app/graphql/paymentIntent/connector.js b/app/graphql/paymentIntent/connector.js new file mode 100644 index 0000000000000000000000000000000000000000..65013ea994c08610ada2e8970b2f587a02ca9bf1 --- /dev/null +++ b/app/graphql/paymentIntent/connector.js @@ -0,0 +1,67 @@ +'use strict'; + +const DataLoader = require('dataloader'); +const { cors } = require('../../../config/plugin'); +const ObjectId = require('mongodb').ObjectID; +const stripe = require("stripe")('process.env.STRIPE_PRIVATE_KEY'); + + +const calculateOrderAmount = (items) => { + // Replace this constant with a calculation of the order's amount + // Calculate the order total on the server to prevent + // people from directly manipulating the amount on the client + return 1400; +}; + +class paymentIntentConnector { + constructor(ctx) { + this.ctx = ctx; + } + + /** + * 创建stripe订单 + * @param {{String}} items 商品对象,包含字符串元素 + * @param {Strin} userId 用户id + * @returns clientSecret + */ + + async createPaymentIntent(items, userId) { + let paymentIntent; + await stripe.paymentIntents.create({ + amount: calculateOrderAmount(items), + currency: "usd", + automatic_payment_methods: { + enabled: true, + }, + }).then(result => { + console.log(result); + paymentIntent = result; + // const user = this.ctx.model.User.findOne({ _id: userId }); + // this.ctx.model.PaymentIntent.create({ + + // }) + + }); + return { clientSecret: paymentIntent.client_secret }; + } + + /** + * 查询stripe订单 + * @param {Strin} id 订单id + * @returns paymentIntentStatus 订单状态 + */ + + async retrievePaymentIntent(id) { + let paymentIntent; + await stripe.paymentIntents.retrieve( + id + ).then(result => { + //console.log(result); + paymentIntent = result; + console.log(paymentIntent); + }); + return { paymentIntentStatus: paymentIntent.status }; + } +} + +module.exports = paymentIntentConnector; diff --git a/app/graphql/paymentIntent/resolver.js b/app/graphql/paymentIntent/resolver.js new file mode 100644 index 0000000000000000000000000000000000000000..d04dc0093bacf0e1f31f823d8b0211d4625f6c4d --- /dev/null +++ b/app/graphql/paymentIntent/resolver.js @@ -0,0 +1,12 @@ +'use strict'; + +module.exports = { + Query: { + createPaymentIntent(root, { items }, ctx) { + return ctx.connector.paymentIntent.createPaymentIntent(items); + }, + retrievePaymentIntent(root, { id }, ctx) { + return ctx.connector.paymentIntent.retrievePaymentIntent(id); + }, + }, +}; diff --git a/app/graphql/paymentIntent/schema.graphql b/app/graphql/paymentIntent/schema.graphql new file mode 100644 index 0000000000000000000000000000000000000000..b968f239775cd93fc48b5d138f0b3d602d2ee48f --- /dev/null +++ b/app/graphql/paymentIntent/schema.graphql @@ -0,0 +1,6 @@ +type clientSecret{ + clientSecret:String, +} +type paymentIntentStatus{ + paymentIntentStatus:String, +} \ No newline at end of file diff --git a/app/graphql/query/schema.graphql b/app/graphql/query/schema.graphql index 79a2f4e20f0be87df930ae9cc5b2d1c0ea21d89d..c4c945c15f77982cadc5e9fcd717a896f45b0830 100644 --- a/app/graphql/query/schema.graphql +++ b/app/graphql/query/schema.graphql @@ -14,6 +14,11 @@ type Query { course(category_id: String!, page: Int, limit: Int): [CourseDetail!] courseCatalogue(course_id: String!): [Catalogue!] courseLink(detail_id: String!): Link! + #stripe paymentIntent支付 + createPaymentIntent(items:Items!):clientSecret! + retrievePaymentIntent(id:String!):paymentIntentStatus! + #stripe refunds支付 + createRefunds(id:String!):paymentIntentStatus! # 获取支付信息 reToken(token: String!): Token! diff --git a/app/graphql/refunds/connector.js b/app/graphql/refunds/connector.js new file mode 100644 index 0000000000000000000000000000000000000000..e2cf0420a0499fa351e5a7642755991282da85ae --- /dev/null +++ b/app/graphql/refunds/connector.js @@ -0,0 +1,35 @@ +'use strict'; + +const DataLoader = require('dataloader'); +const { cors } = require('../../../config/plugin'); +const ObjectId = require('mongodb').ObjectID; +const stripe = require("stripe")('process.env.STRIPE_PRIVATE_KEY'); + +class refundsConnector { + constructor(ctx) { + this.ctx = ctx; + } + + /** + * 创建退款 + * @param {String} id 订单的payment_intent的id + * @returns paymentIntentStatus 退款状态 + */ + + async createRefunds(id) { + let paymentIntent; + await stripe.refunds.create({ + payment_intent: id, + }).then(result => { + //console.log(result); + paymentIntent = result; + }); + console.log('---------------------'); + console.log(paymentIntent); + console.log('---------------------'); + return { paymentIntentStatus: paymentIntent.status }; + } + +} + +module.exports = refundsConnector; diff --git a/app/graphql/refunds/resolver.js b/app/graphql/refunds/resolver.js new file mode 100644 index 0000000000000000000000000000000000000000..624ccba1f968a3ba74fbc079363f89527296f7a9 --- /dev/null +++ b/app/graphql/refunds/resolver.js @@ -0,0 +1,9 @@ +'use strict'; + +module.exports = { + Query: { + createRefunds(root, { id }, ctx) { + return ctx.connector.refunds.createRefunds(id); + }, + }, +}; diff --git a/app/graphql/refunds/schema.graphql b/app/graphql/refunds/schema.graphql new file mode 100644 index 0000000000000000000000000000000000000000..b968f239775cd93fc48b5d138f0b3d602d2ee48f --- /dev/null +++ b/app/graphql/refunds/schema.graphql @@ -0,0 +1,6 @@ +type clientSecret{ + clientSecret:String, +} +type paymentIntentStatus{ + paymentIntentStatus:String, +} \ No newline at end of file diff --git a/app/model/paymentIntent.js b/app/model/paymentIntent.js new file mode 100644 index 0000000000000000000000000000000000000000..56f726375ae41642bbd373eb9b8723dfdb1ac5c2 --- /dev/null +++ b/app/model/paymentIntent.js @@ -0,0 +1,84 @@ +module.exports = app => { + const mongoose = app.mongoose; + const Schema = mongoose.Schema; + const PaymentIntentSchema = new Schema({ + amount: { + type: String, + unique: false, + required: false, + }, + currency: { + type: String, + unique: false, + required: false, + }, + description: { + type: String, + unique: false, + required: false, + default: '', + }, + id: { + type: String, + unique: false, + required: false, + }, + userId: { + type: String, + unique: false, + required: false, + },//customer为该项对应值 + userPhone: { + type: String, + unique: false, + required: false, + }, + receipt_email: { + type: String, + unique: false, + required: false, + }, + ////////////////////////////////////////////////// + payTime: { + type: String, + unique: false, + required: false, + }, + refundTime: { + type: String, + unique: false, + required: false, + }, + subject: { + type: String, + unique: false, + required: false, + default: '', + }, + orderInfo: { + type: String, + unique: false, + required: false, + default: '', + }, + status: { + type: String, + unique: false, + required: false, + default: 'WAIT_BUYER_PAY', + }, + refundReason: { + type: String, + unique: false, + required: false, + default: '', + }, + refundDescription: { + type: String, + unique: false, + required: false, + default: '', + }, + }); + return mongoose.model('PaymentIntent', PaymentIntentSchema); +}; diff --git a/app/router.js b/app/router.js index d94ed54dec29a89581fb05a6b61a9bd0df881d20..42577d06bb46fe551d1afef0d954c737770ceacd 100644 --- a/app/router.js +++ b/app/router.js @@ -4,17 +4,18 @@ * @param {Egg.Application} app - egg application */ module.exports = app => { - const { router, controller } = app; - router.get('/', controller.home.index); + const { router, controller } = app; + router.get('/', controller.home.index); - // 解析XML的中间件 - const xmlParseMiddleware = app.middleware.xmlParse(); + // 解析XML的中间件 + const xmlParseMiddleware = app.middleware.xmlParse(); - // 调用支付_web - router.get('/aliPay', controller.default.aliPay.pay); - // 支付回调 - router.get('/aliPay/aliPayReturn', controller.default.aliPay.aliPayReturn); - // 支付通知(关闭CSRF验证) - router.post('/aliPay/aliPayNotify', xmlParseMiddleware, controller.default.aliPay.aliPayNotify); + // 调用支付_web + router.get('/aliPay', controller.default.aliPay.pay); + // 支付回调 + router.get('/aliPay/aliPayReturn', controller.default.aliPay.aliPayReturn); + // 支付通知(关闭CSRF验证) + router.post('/aliPay/aliPayNotify', xmlParseMiddleware, controller.default.aliPay.aliPayNotify); + router.post('/webhook', controller.stripe.webhook); }; diff --git a/app/service/stripe.js b/app/service/stripe.js new file mode 100644 index 0000000000000000000000000000000000000000..29d87e29a86f40c2445c859408ccb9a11be58898 --- /dev/null +++ b/app/service/stripe.js @@ -0,0 +1,35 @@ +'use strict'; + +const Service = require('egg').Service; +const stripe = require("stripe")(process.env.STRIPE_PRIVATE_KEY); + + +class WebhookService extends Service { + + // 异步通知 + webhook(event) { + // 实例化支付宝支付 + console.log("receive webhook"); + switch (event.type) { + case 'payment_intent.succeeded': + const paymentIntent = event.data.object; + console.log('PaymentIntent was successful!'); + break; + case 'payment_method.attached': + const paymentMethod = event.data.object; + console.log('PaymentMethod was attached to a Customer!'); + break; + // ... handle other event types + case 'payment_intent.created': + //const paymentMethod = event.data.object; + console.log('PaymentMethod was precreated!'); + break; + default: + console.log(`Unhandled event type ${event.type}`); + } + } + + +} + +module.exports = WebhookService; diff --git a/config/config.default.js b/config/config.default.js index 88b7f02e76977f1140520de258bba57c566b20b7..828dfc61f6495757e279219c33714c4586a15446 100644 --- a/config/config.default.js +++ b/config/config.default.js @@ -78,7 +78,7 @@ module.exports = appInfo => { ignore: ctx => { // console.log("____________________________________________________"); // console.log(ctx.request.url); - if (ctx.request.url === '/aliPay/aliPayNotify' || ctx.request.url === '/graphql' || ctx.request.url === '/graphql?' || ctx.request.url === '/api/registered' || ctx.request.url === '/api/login') { + if (ctx.request.url === '/aliPay/aliPayNotify' || ctx.request.url === '/graphql' || ctx.request.url === '/graphql?' || ctx.request.url === '/api/registered' || ctx.request.url === '/api/login' || ctx.request.url === '/webhook') { return true; } return false; diff --git a/package.json b/package.json index 478fb1214c3ea4348416aa888153d0fd36662b5d..e27916abb01ab150d27b64c7c4991e8a0ceb27d1 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "nodemailer": "^6.8.0", "qiniu": "^7.7.0", "redis": "^4.3.1", + "stripe": "^11.17.0", "validator": "^13.9.0" }, "devDependencies": {