parent
28a2c991a2
commit
a8a9141ede
@ -1,13 +0,0 @@ |
|||||||
package features_fakepay |
|
||||||
|
|
||||||
import ( |
|
||||||
"github.com/gofiber/fiber/v2" |
|
||||||
) |
|
||||||
|
|
||||||
func PostApiPay(c *fiber.Ctx) error { |
|
||||||
return c.Redirect("/fakepay/complete") |
|
||||||
} |
|
||||||
|
|
||||||
func SetupApi(app *fiber.App) { |
|
||||||
app.Post("/api/fakepay/pay", PostApiPay) |
|
||||||
} |
|
||||||
@ -1,9 +0,0 @@ |
|||||||
package features_fakepay |
|
||||||
|
|
||||||
import ( |
|
||||||
// "MY/webapp/data"
|
|
||||||
// _ "github.com/mattn/go-sqlite3"
|
|
||||||
// sq "github.com/Masterminds/squirrel"
|
|
||||||
) |
|
||||||
|
|
||||||
|
|
||||||
@ -1,10 +0,0 @@ |
|||||||
package features_fakepay |
|
||||||
|
|
||||||
import ( |
|
||||||
"github.com/gofiber/fiber/v2" |
|
||||||
) |
|
||||||
|
|
||||||
func Setup(app *fiber.App) { |
|
||||||
SetupApi(app) |
|
||||||
SetupViews(app) |
|
||||||
} |
|
||||||
@ -1,11 +0,0 @@ |
|||||||
package features_fakepay |
|
||||||
|
|
||||||
import ( |
|
||||||
"github.com/gofiber/fiber/v2" |
|
||||||
. "MY/webapp/common" |
|
||||||
) |
|
||||||
|
|
||||||
func SetupViews(app *fiber.App) { |
|
||||||
err := ConfigViews(app, "views/fakepay") |
|
||||||
if err != nil { panic(err) } |
|
||||||
} |
|
||||||
@ -1,78 +0,0 @@ |
|||||||
package features_paypal |
|
||||||
|
|
||||||
import ( |
|
||||||
"github.com/gofiber/fiber/v2" |
|
||||||
"github.com/plutov/paypal/v4" |
|
||||||
"os" |
|
||||||
"context" |
|
||||||
. "MY/webapp/common" |
|
||||||
config "MY/webapp/config" |
|
||||||
"fmt" |
|
||||||
) |
|
||||||
|
|
||||||
func CreatePaypal() (*paypal.Client, error) { |
|
||||||
return paypal.NewClient( |
|
||||||
config.Settings.Paypal.ClientID, |
|
||||||
config.Settings.Paypal.SecretID, |
|
||||||
config.Settings.Paypal.URL) // or paypal.APIBaseLive
|
|
||||||
} |
|
||||||
|
|
||||||
func PostApiOrder(c *fiber.Ctx) error { |
|
||||||
pay, err := CreatePaypal() |
|
||||||
|
|
||||||
if err != nil { return IfErrNil(err, c) } |
|
||||||
|
|
||||||
pay.SetLog(os.Stdout) |
|
||||||
|
|
||||||
units := []paypal.PurchaseUnitRequest{ |
|
||||||
{ |
|
||||||
ReferenceID: "myinternalid1", |
|
||||||
Amount: &paypal.PurchaseUnitAmount{ |
|
||||||
Currency: "USD", |
|
||||||
Value: "10.99", |
|
||||||
}, |
|
||||||
Description: "Product description", |
|
||||||
Items: []paypal.Item{ |
|
||||||
{ |
|
||||||
Name: "Learn Go the Hard Way", |
|
||||||
UnitAmount: &paypal.Money{ |
|
||||||
Currency: "USD", |
|
||||||
Value: "10.99", |
|
||||||
}, |
|
||||||
Quantity: "1", |
|
||||||
}, |
|
||||||
}, |
|
||||||
}, |
|
||||||
} |
|
||||||
|
|
||||||
source := &paypal.PaymentSource{} |
|
||||||
appCtx := &paypal.ApplicationContext{} |
|
||||||
|
|
||||||
order, err := pay.CreateOrder(context.TODO(), paypal.OrderIntentCapture, units, source, appCtx) |
|
||||||
|
|
||||||
fmt.Println("ORDER", order) |
|
||||||
|
|
||||||
return c.JSON(order) |
|
||||||
} |
|
||||||
|
|
||||||
func PostApiOrderCapture(c *fiber.Ctx) error { |
|
||||||
orderID := c.Params("orderID") |
|
||||||
|
|
||||||
fmt.Println("POST ORDER CAPTURE", orderID) |
|
||||||
pay, err := CreatePaypal() |
|
||||||
if err != nil { return IfErrNil(err, c) } |
|
||||||
|
|
||||||
capture, err := pay.CaptureOrder(context.TODO(), orderID, paypal.CaptureOrderRequest{}) |
|
||||||
if err != nil { return IfErrNil(err, c) } |
|
||||||
|
|
||||||
fmt.Println("CAPTURE", capture) |
|
||||||
|
|
||||||
return c.JSON(fiber.Map{"status": "ok"}) |
|
||||||
} |
|
||||||
|
|
||||||
func SetupApi(app *fiber.App) { |
|
||||||
app.Post("/api/paypal/order", PostApiOrder) |
|
||||||
app.Post("/api/paypal/order/:orderID/capture", PostApiOrderCapture) |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
@ -1,160 +0,0 @@ |
|||||||
import express from "express"; |
|
||||||
import "dotenv/config"; |
|
||||||
import { |
|
||||||
ApiError, |
|
||||||
CheckoutPaymentIntent, |
|
||||||
Client, |
|
||||||
Environment, |
|
||||||
LogLevel, |
|
||||||
OrdersController, |
|
||||||
PaymentsController, |
|
||||||
PaypalExperienceLandingPage, |
|
||||||
PaypalExperienceUserAction, |
|
||||||
ShippingPreference, |
|
||||||
} from "@paypal/paypal-server-sdk"; |
|
||||||
import bodyParser from "body-parser"; |
|
||||||
|
|
||||||
const app = express(); |
|
||||||
app.use(bodyParser.json()); |
|
||||||
|
|
||||||
const { |
|
||||||
PAYPAL_CLIENT_ID, |
|
||||||
PAYPAL_CLIENT_SECRET, |
|
||||||
PORT = 8080, |
|
||||||
} = process.env; |
|
||||||
|
|
||||||
const client = new Client({ |
|
||||||
clientCredentialsAuthCredentials: { |
|
||||||
oAuthClientId: PAYPAL_CLIENT_ID, |
|
||||||
oAuthClientSecret: PAYPAL_CLIENT_SECRET, |
|
||||||
}, |
|
||||||
timeout: 0, |
|
||||||
environment: Environment.Sandbox, |
|
||||||
logging: { |
|
||||||
logLevel: LogLevel.Info, |
|
||||||
logRequest: { logBody: true }, |
|
||||||
logResponse: { logHeaders: true }, |
|
||||||
}, |
|
||||||
}); |
|
||||||
|
|
||||||
const ordersController = new OrdersController(client); |
|
||||||
const paymentsController = new PaymentsController(client); |
|
||||||
|
|
||||||
|
|
||||||
/** |
|
||||||
* Create an order to start the transaction. |
|
||||||
* @see https://developer.paypal.com/docs/api/orders/v2/#orders_create
|
|
||||||
*/ |
|
||||||
const createOrder = async (cart) => { |
|
||||||
const collect = { |
|
||||||
body: { |
|
||||||
intent: "CAPTURE", |
|
||||||
purchaseUnits: [ |
|
||||||
{ |
|
||||||
amount: { |
|
||||||
currencyCode: "USD", |
|
||||||
value: "100", |
|
||||||
breakdown: { |
|
||||||
itemTotal: { |
|
||||||
currencyCode: "USD", |
|
||||||
value: "100", |
|
||||||
}, |
|
||||||
}, |
|
||||||
}, |
|
||||||
// lookup item details in `cart` from database
|
|
||||||
items: [ |
|
||||||
{ |
|
||||||
name: "T-Shirt", |
|
||||||
unitAmount: { |
|
||||||
currencyCode: "USD", |
|
||||||
value: "100", |
|
||||||
}, |
|
||||||
quantity: "1", |
|
||||||
description: "Super Fresh Shirt", |
|
||||||
sku: "sku01", |
|
||||||
}, |
|
||||||
], |
|
||||||
}, |
|
||||||
], |
|
||||||
}, |
|
||||||
prefer: "return=minimal", |
|
||||||
}; |
|
||||||
|
|
||||||
|
|
||||||
try { |
|
||||||
const { body, ...httpResponse } = await ordersController.createOrder( |
|
||||||
collect |
|
||||||
); |
|
||||||
// Get more response info...
|
|
||||||
// const { statusCode, headers } = httpResponse;
|
|
||||||
return { |
|
||||||
jsonResponse: JSON.parse(body), |
|
||||||
httpStatusCode: httpResponse.statusCode, |
|
||||||
}; |
|
||||||
} catch (error) { |
|
||||||
if (error instanceof ApiError) { |
|
||||||
// const { statusCode, headers } = error;
|
|
||||||
throw new Error(error.message); |
|
||||||
} |
|
||||||
} |
|
||||||
}; |
|
||||||
|
|
||||||
// createOrder route
|
|
||||||
app.post("/api/orders", async (req, res) => { |
|
||||||
try { |
|
||||||
// use the cart information passed from the front-end to calculate the order amount detals
|
|
||||||
const { cart } = req.body; |
|
||||||
const { jsonResponse, httpStatusCode } = await createOrder(cart); |
|
||||||
res.status(httpStatusCode).json(jsonResponse); |
|
||||||
} catch (error) { |
|
||||||
console.error("Failed to create order:", error); |
|
||||||
res.status(500).json({ error: "Failed to create order." }); |
|
||||||
} |
|
||||||
}); |
|
||||||
|
|
||||||
|
|
||||||
/** |
|
||||||
* Capture payment for the created order to complete the transaction. |
|
||||||
* @see https://developer.paypal.com/docs/api/orders/v2/#orders_capture
|
|
||||||
*/ |
|
||||||
const captureOrder = async (orderID) => { |
|
||||||
const collect = { |
|
||||||
id: orderID, |
|
||||||
prefer: "return=minimal", |
|
||||||
}; |
|
||||||
|
|
||||||
try { |
|
||||||
const { body, ...httpResponse } = await ordersController.captureOrder( |
|
||||||
collect |
|
||||||
); |
|
||||||
// Get more response info...
|
|
||||||
// const { statusCode, headers } = httpResponse;
|
|
||||||
return { |
|
||||||
jsonResponse: JSON.parse(body), |
|
||||||
httpStatusCode: httpResponse.statusCode, |
|
||||||
}; |
|
||||||
} catch (error) { |
|
||||||
if (error instanceof ApiError) { |
|
||||||
// const { statusCode, headers } = error;
|
|
||||||
throw new Error(error.message); |
|
||||||
} |
|
||||||
} |
|
||||||
}; |
|
||||||
|
|
||||||
// captureOrder route
|
|
||||||
app.post("/api/orders/:orderID/capture", async (req, res) => { |
|
||||||
try { |
|
||||||
const { orderID } = req.params; |
|
||||||
const { jsonResponse, httpStatusCode } = await captureOrder(orderID); |
|
||||||
res.status(httpStatusCode).json(jsonResponse); |
|
||||||
} catch (error) { |
|
||||||
console.error("Failed to create order:", error); |
|
||||||
res.status(500).json({ error: "Failed to capture order." }); |
|
||||||
} |
|
||||||
}); |
|
||||||
|
|
||||||
|
|
||||||
app.listen(PORT, () => { |
|
||||||
console.log(`Node server listening at http://localhost:${PORT}/`); |
|
||||||
}); |
|
||||||
|
|
||||||
@ -1,9 +0,0 @@ |
|||||||
package features_paypal |
|
||||||
|
|
||||||
import ( |
|
||||||
// "MY/webapp/data"
|
|
||||||
// _ "github.com/mattn/go-sqlite3"
|
|
||||||
// sq "github.com/Masterminds/squirrel"
|
|
||||||
) |
|
||||||
|
|
||||||
|
|
||||||
@ -1,10 +0,0 @@ |
|||||||
package features_paypal |
|
||||||
|
|
||||||
import ( |
|
||||||
"github.com/gofiber/fiber/v2" |
|
||||||
) |
|
||||||
|
|
||||||
func Setup(app *fiber.App) { |
|
||||||
SetupApi(app) |
|
||||||
SetupViews(app) |
|
||||||
} |
|
||||||
@ -1,11 +0,0 @@ |
|||||||
package features_paypal |
|
||||||
|
|
||||||
import ( |
|
||||||
"github.com/gofiber/fiber/v2" |
|
||||||
. "MY/webapp/common" |
|
||||||
) |
|
||||||
|
|
||||||
func SetupViews(app *fiber.App) { |
|
||||||
err := ConfigViews(app, "views/paypal") |
|
||||||
if err != nil { panic(err) } |
|
||||||
} |
|
||||||
@ -1,31 +0,0 @@ |
|||||||
package features_shopping |
|
||||||
|
|
||||||
import ( |
|
||||||
"github.com/gofiber/fiber/v2" |
|
||||||
"MY/webapp/data" |
|
||||||
_ "github.com/mattn/go-sqlite3" |
|
||||||
sq "github.com/Masterminds/squirrel" |
|
||||||
"fmt" |
|
||||||
) |
|
||||||
|
|
||||||
func GetApiProducts(c *fiber.Ctx) error { |
|
||||||
sql, args, err := sq.Select("*").From("Product").ToSql() |
|
||||||
|
|
||||||
fmt.Println("SQL:", sql, args, err) |
|
||||||
|
|
||||||
return data.SelectJson[data.Product](c, err, sql, args...) |
|
||||||
} |
|
||||||
|
|
||||||
func GetApiCart(c *fiber.Ctx) error { |
|
||||||
return c.JSON(fiber.Map{}) |
|
||||||
} |
|
||||||
|
|
||||||
func GetApiCartRemove(c *fiber.Ctx) error { |
|
||||||
return c.Redirect("/shopping/checkout") |
|
||||||
} |
|
||||||
|
|
||||||
func SetupApi(app *fiber.App) { |
|
||||||
app.Get("/api/shopping/products", GetApiProducts) |
|
||||||
app.Get("/api/shopping/cart", GetApiCart) |
|
||||||
app.Get("/api/shopping/cart/remove/:item_id", GetApiCartRemove) |
|
||||||
} |
|
||||||
@ -1,9 +0,0 @@ |
|||||||
package features_shopping |
|
||||||
|
|
||||||
import ( |
|
||||||
// "MY/webapp/data"
|
|
||||||
// _ "github.com/mattn/go-sqlite3"
|
|
||||||
// sq "github.com/Masterminds/squirrel"
|
|
||||||
) |
|
||||||
|
|
||||||
|
|
||||||
@ -1,10 +0,0 @@ |
|||||||
package features_shopping |
|
||||||
|
|
||||||
import ( |
|
||||||
"github.com/gofiber/fiber/v2" |
|
||||||
) |
|
||||||
|
|
||||||
func Setup(app *fiber.App) { |
|
||||||
SetupApi(app) |
|
||||||
SetupViews(app) |
|
||||||
} |
|
||||||
@ -1,11 +0,0 @@ |
|||||||
package features_shopping |
|
||||||
|
|
||||||
import ( |
|
||||||
"github.com/gofiber/fiber/v2" |
|
||||||
. "MY/webapp/common" |
|
||||||
) |
|
||||||
|
|
||||||
func SetupViews(app *fiber.App) { |
|
||||||
err := ConfigViews(app, "views/shopping") |
|
||||||
if err != nil { panic(err) } |
|
||||||
} |
|
||||||
@ -1,9 +1,44 @@ |
|||||||
package features_survey |
package features_survey |
||||||
|
|
||||||
import ( |
import ( |
||||||
// "MY/webapp/data"
|
"MY/webapp/data" |
||||||
// _ "github.com/mattn/go-sqlite3"
|
// _ "github.com/mattn/go-sqlite3"
|
||||||
// sq "github.com/Masterminds/squirrel"
|
// sq "github.com/Masterminds/squirrel"
|
||||||
) |
) |
||||||
|
|
||||||
|
func QueryData() data.Survey { |
||||||
|
return data.Survey{ |
||||||
|
Title: "Sample Survey", |
||||||
|
Description: "The description.", |
||||||
|
CreatedOn: "01/20/26", |
||||||
|
Respondents: 200, |
||||||
|
State: "Finished", |
||||||
|
Questions: []data.Question{ |
||||||
|
{ |
||||||
|
Id: 1, |
||||||
|
Question: "Do you like HTML?", |
||||||
|
Answer: true, |
||||||
|
// TODO: just for prototyping, needs to be a better model
|
||||||
|
Result: 20, |
||||||
|
}, |
||||||
|
{ |
||||||
|
Id: 2, |
||||||
|
Question: "Do you like CSS?", |
||||||
|
Answer: false, |
||||||
|
Result: 11, |
||||||
|
}, |
||||||
|
{ |
||||||
|
Id: 3, |
||||||
|
Question: "Do you like JavaScript?", |
||||||
|
Answer: true, |
||||||
|
Result: 43, |
||||||
|
}, |
||||||
|
{ |
||||||
|
Id: 4, |
||||||
|
Question: "Are you a programmer?", |
||||||
|
Answer: true, |
||||||
|
Result: 28, |
||||||
|
}, |
||||||
|
}, |
||||||
|
} |
||||||
|
} |
||||||
|
|||||||
@ -0,0 +1,21 @@ |
|||||||
|
-- +goose Up |
||||||
|
-- +goose StatementBegin |
||||||
|
CREATE TABLE question ( |
||||||
|
id INTEGER PRIMARY KEY, |
||||||
|
question TEXT UNIQUE NOT NULL, |
||||||
|
result INTEGER DEFAULT 0); |
||||||
|
|
||||||
|
CREATE TABLE survey ( |
||||||
|
id INTEGER PRIMARY KEY, |
||||||
|
title TEXT UNIQUE NOT NULL, |
||||||
|
description TEXT UNIQUE NOT NULL, |
||||||
|
created_on DATETIME DEFAULT CURRENT_TIMESTAMP, |
||||||
|
respondents INTEGER DEFAULT 0, |
||||||
|
active BOOLEAN INTEGER DEFAULT false); |
||||||
|
-- +goose StatementEnd |
||||||
|
|
||||||
|
-- +goose Down |
||||||
|
-- +goose StatementBegin |
||||||
|
DROP TABLE IF EXISTS question; |
||||||
|
DROP TABLE IF EXISTS survey; |
||||||
|
-- +goose StatementEnd |
||||||
Loading…
Reference in new issue