Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 8 additions & 58 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,72 +1,22 @@
# Payment service
## payment-service
- **Purpose:** Stripe integration (Checkout Session + webhooks).
- **Base path:** `/api/payments`

## Create payment database + user on mysql server
### Create product database
```
kubectl -n cloudshopt exec -it cloudshopt-mysql-0 -- bash

mysql -u root -prootpass
```
# mysql -u root -prootpass

```
CREATE DATABASE cloudshopt_payments CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'users'@'%' IDENTIFIED BY 'userspass';
CREATE USER 'users'@'%' IDENTIFIED BY 'CHANGE_ME_PASSWORD';
GRANT ALL PRIVILEGES ON cloudshopt_payments.* TO 'users'@'%';
FLUSH PRIVILEGES;
```

Ustvari še bazo za *dev* okolje
```
CREATE DATABASE cloudshopt_payments_dev CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'users_dev'@'%' IDENTIFIED BY 'userspass';
GRANT ALL PRIVILEGES ON cloudshopt_payments_dev.* TO 'users_dev'@'%';
FLUSH PRIVILEGES;
```

## Crete external secrets for prod and dev
prod:
```
kubectl -n cloudshopt create secret generic payment-service-secrets \
--from-literal=DB_PASSWORD="userspass" \
--from-literal=REDIS_PASSWORD="redispass" \
--dry-run=client -o yaml | kubectl apply -f -
```

dev:
```
kubectl -n cloudshopt-dev create secret generic payment-service-secrets \
--from-literal=DB_PASSWORD="userspass" \
--from-literal=REDIS_PASSWORD="redispass" \
--dry-run=client -o yaml | kubectl apply -f -
```

check for secrets:
```
kubectl get secret -n cloudshopt payment-service-secrets
kubectl get secret -n cloudshopt-dev payment-service-secrets
```

## Install payment-service for prod and dev
prod:
```
helm upgrade --install payment-service ./helm/payment-service \
-n cloudshopt \
-f helm/payment-service/values.yaml
```

dev:
```
helm upgrade --install payment-service-dev ./helm/payment-service \
-n cloudshopt-dev \
-f helm/payment-service/values-dev.yaml
```

### Migrations


## Migrations

run migrations:
```
kubectl exec -n cloudshopt-dev -it deploy/payment-service-dev -c app -- sh

kubectl exec -n cloudshopt -it deploy/payment-service -c app -- sh
# php artisan migrate
```
207 changes: 207 additions & 0 deletions docs/openapi.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
openapi: 3.0.3
info:
title: CloudShopt Payment Service API
version: 1.0.0
tags:
- name: Health
- name: Diagnostics
- name: Checkout
- name: Webhooks

paths:
/healthz:
get:
tags: [Health]
summary: Health check
responses:
"200":
description: OK
content:
application/json:
schema:
$ref: "#/components/schemas/StatusOkBool"

/info:
get:
tags: [Diagnostics]
summary: Service info (sha, time)
responses:
"200":
description: Info
content:
application/json:
schema:
$ref: "#/components/schemas/InfoResponse"

/database:
get:
tags: [Diagnostics]
summary: Database connectivity check (optional)
responses:
"200":
description: DB OK (or not used)
content:
application/json:
schema:
$ref: "#/components/schemas/DatabaseOkResponse"
"500":
description: DB connection failed
content:
application/json:
schema:
$ref: "#/components/schemas/DatabaseFailResponse"

/checkout-session:
post:
tags: [Checkout]
summary: Create Stripe Checkout Session (JWT)
description: Returns Stripe hosted checkout URL. Amount is derived from order total (cents).
security:
- bearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/CreateCheckoutSessionRequest"
responses:
"200":
description: Session created
content:
application/json:
schema:
$ref: "#/components/schemas/CreateCheckoutSessionResponse"
"401":
$ref: "#/components/responses/Unauthorized"
"404":
$ref: "#/components/responses/NotFound"
"422":
$ref: "#/components/responses/ValidationError"

/webhooks/stripe:
post:
tags: [Webhooks]
summary: Stripe webhook receiver
description: Stripe calls this endpoint. Signature is verified using STRIPE_WEBHOOK_SECRET.
requestBody:
required: true
content:
application/json:
schema:
type: object
responses:
"200":
description: Received
content:
application/json:
schema:
$ref: "#/components/schemas/WebhookReceived"
"400":
description: Invalid signature / bad request
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorMessage"

components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer
bearerFormat: JWT

responses:
Unauthorized:
description: Unauthorized
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorMessage"

NotFound:
description: Not found
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorMessage"

ValidationError:
description: Validation error
content:
application/json:
schema:
$ref: "#/components/schemas/ValidationError"

schemas:
ErrorMessage:
type: object
required: [message]
properties:
message: { type: string }

ValidationError:
type: object
required: [message, errors]
properties:
message: { type: string }
errors:
type: object
additionalProperties:
type: array
items: { type: string }

StatusOkBool:
type: object
required: [ok]
properties:
ok: { type: boolean, example: true }

InfoResponse:
type: object
required: [ok, service, sha, time]
properties:
ok: { type: boolean, example: true }
service: { type: string, example: payment-service }
sha: { type: string, nullable: true, example: 0.0.1 }
time: { type: string, format: date-time, example: "2026-01-29T10:15:30.123Z" }

DatabaseOkResponse:
type: object
required: [ok, db, time]
properties:
ok: { type: boolean, example: true }
db:
type: object
required: [connection, database, ping_ms]
properties:
connection: { type: string, example: mysql }
database: { type: string, example: cloudshopt_payments_dev }
ping_ms: { type: number, format: float, example: 1.25 }
time: { type: string, format: date-time, example: "2026-01-29T10:15:30.123Z" }

DatabaseFailResponse:
type: object
required: [ok, error, message]
properties:
ok: { type: boolean, example: false }
error: { type: string, example: DB connection failed }
message: { type: string, nullable: true, example: "SQLSTATE[HY000] ..." }

CreateCheckoutSessionRequest:
type: object
required: [order_id]
properties:
order_id: { type: integer, minimum: 1, example: 10 }

CreateCheckoutSessionResponse:
type: object
required: [url, session_id]
properties:
url: { type: string, example: https://checkout.stripe.com/c/pay/cs_test_... }
session_id: { type: string, example: cs_test_a11lqwy13iFvK69o... }

WebhookReceived:
type: object
required: [received]
properties:
received: { type: boolean, example: true }
22 changes: 22 additions & 0 deletions resources/views/swagger.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>API Docs</title>
<link rel="stylesheet" href="https://unpkg.com/swagger-ui-dist@5/swagger-ui.css" />
</head>
<body>
<div id="swagger-ui"></div>

<script src="https://unpkg.com/swagger-ui-dist@5/swagger-ui-bundle.js"></script>
<script>
window.onload = () => {
SwaggerUIBundle({
url: "/api/payments/openapi.yaml",
dom_id: "#swagger-ui",
});
};
</script>
</body>
</html>
15 changes: 15 additions & 0 deletions routes/api.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,21 @@
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Route;

Route::get('/openapi.yaml', function () {
$path = base_path('docs/openapi.yaml');

abort_unless(file_exists($path), 404, 'openapi.yaml not found');

return response()->file($path, [
'Content-Type' => 'application/yaml; charset=utf-8',
'Cache-Control' => 'no-store',
]);
});

Route::get('/docs', function () {
return response()->view('swagger');
});

Route::get('/info', function () {
return response()->json([
'ok11' => true,
Expand Down