@medalsocial/sdk 1.9.0 → 1.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -988,6 +988,98 @@ paths:
988
988
  $ref: "#/components/schemas/ApiResponse_BookingActionResult"
989
989
  default:
990
990
  $ref: "#/components/responses/ApiError"
991
+ /api/v1/bookings/{id}/payment:
992
+ parameters:
993
+ - $ref: "#/components/parameters/Id"
994
+ post:
995
+ tags: [Bookings]
996
+ operationId: startBookingPayment
997
+ summary: Start a Vipps payment for a booking
998
+ description: >-
999
+ Reserves (or charges) the booking's amount and returns the wallet
1000
+ redirect URL. The customer must have accepted your terms before this
1001
+ call — `terms_accepted` must be literally `true`, and a body omitting
1002
+ it is a 400. One live payment per booking: a second while one is
1003
+ outstanding answers 409, as does a booking that is not payable or a
1004
+ refusal from Vipps. A `return_url` this workspace's own sites do not
1005
+ vouch for answers 422, not 400 — the URL parses, it is just not yours.
1006
+ 403 when the payments switch or the bookings module is off.
1007
+ requestBody:
1008
+ required: true
1009
+ content:
1010
+ application/json:
1011
+ schema:
1012
+ $ref: "#/components/schemas/StartBookingPaymentInput"
1013
+ responses:
1014
+ "200":
1015
+ description: >-
1016
+ The reference and the SHOW-ONCE redirect URL. The URL is never
1017
+ returned again — the payment behind it expires after ten minutes,
1018
+ so a cached one leads into a payment that no longer exists.
1019
+ content:
1020
+ application/json:
1021
+ schema:
1022
+ $ref: "#/components/schemas/ApiResponse_BookingPaymentStart"
1023
+ default:
1024
+ $ref: "#/components/responses/ApiError"
1025
+ get:
1026
+ tags: [Bookings]
1027
+ operationId: getBookingPayment
1028
+ summary: Read the newest payment attempt on a booking
1029
+ description: >-
1030
+ Returns the newest attempt only. A booking with no payment yet, an
1031
+ unknown booking id and another workspace's booking all answer 404
1032
+ alike, so this is not an existence oracle.
1033
+ responses:
1034
+ "200":
1035
+ description: The payment. `redirect_url` is deliberately absent here.
1036
+ content:
1037
+ application/json:
1038
+ schema:
1039
+ $ref: "#/components/schemas/ApiResponse_BookingPayment"
1040
+ default:
1041
+ $ref: "#/components/responses/ApiError"
1042
+ /api/v1/bookings/manage/{token}/payment:
1043
+ parameters:
1044
+ - $ref: "#/components/parameters/ManageToken"
1045
+ post:
1046
+ tags: [Bookings]
1047
+ operationId: startManageBookingPayment
1048
+ summary: Start a payment on the customer's behalf
1049
+ description: >-
1050
+ The manage-token twin of `startBookingPayment` — same body, same
1051
+ responses; only the credential that authorizes it differs.
1052
+ requestBody:
1053
+ required: true
1054
+ content:
1055
+ application/json:
1056
+ schema:
1057
+ $ref: "#/components/schemas/StartBookingPaymentInput"
1058
+ responses:
1059
+ "200":
1060
+ description: The reference and the show-once redirect URL.
1061
+ content:
1062
+ application/json:
1063
+ schema:
1064
+ $ref: "#/components/schemas/ApiResponse_BookingPaymentStart"
1065
+ default:
1066
+ $ref: "#/components/responses/ApiError"
1067
+ get:
1068
+ tags: [Bookings]
1069
+ operationId: getManageBookingPayment
1070
+ summary: Read the payment on the customer's own booking
1071
+ description: >-
1072
+ An unknown token, another workspace's token and a booking with no
1073
+ payment yet all answer 404 alike.
1074
+ responses:
1075
+ "200":
1076
+ description: The payment. `redirect_url` is deliberately absent here.
1077
+ content:
1078
+ application/json:
1079
+ schema:
1080
+ $ref: "#/components/schemas/ApiResponse_BookingPayment"
1081
+ default:
1082
+ $ref: "#/components/responses/ApiError"
991
1083
  /api/v1/bookings/manage/{token}:
992
1084
  parameters:
993
1085
  - $ref: "#/components/parameters/ManageToken"
@@ -2785,6 +2877,19 @@ components:
2785
2877
  BookingPaymentStatus:
2786
2878
  type: string
2787
2879
  enum: [none, reserved, captured, refunded]
2880
+ BookingPaymentMode:
2881
+ description: >-
2882
+ What a booking must have paid before the business honours it: nothing,
2883
+ a reservation captured later, or the full amount up front.
2884
+ type: string
2885
+ enum: [none, reserve, prepay]
2886
+ BookingPaymentState:
2887
+ description: >-
2888
+ The state of one payment attempt, in Medal's vocabulary rather than the
2889
+ wallet's — a Vipps payment stays AUTHORIZED after a capture, so read
2890
+ the øre aggregates to learn what actually moved.
2891
+ type: string
2892
+ enum: [created, authorized, captured, cancelled, refunded, failed, expired]
2788
2893
  BookingCreatedVia:
2789
2894
  type: string
2790
2895
  enum: [web, dashboard, walk_in, api]
@@ -2811,6 +2916,7 @@ components:
2811
2916
  - cancel_reason
2812
2917
  - rescheduled_from_id
2813
2918
  - payment_status
2919
+ - payment_mode
2814
2920
  - amount_ore
2815
2921
  - notes
2816
2922
  - internal_notes
@@ -2862,6 +2968,12 @@ components:
2862
2968
  type: [string, "null"]
2863
2969
  payment_status:
2864
2970
  $ref: "#/components/schemas/BookingPaymentStatus"
2971
+ payment_mode:
2972
+ description: >-
2973
+ What this booking required when it was made — frozen at creation.
2974
+ `payment_status: none` cannot tell "owes nothing" from "has not
2975
+ paid yet"; this can.
2976
+ $ref: "#/components/schemas/BookingPaymentMode"
2865
2977
  amount_ore:
2866
2978
  description: Price in integer øre. Never a float and never kroner.
2867
2979
  type: [integer, "null"]
@@ -2895,6 +3007,7 @@ components:
2895
3007
  - weekend_surcharge_pct
2896
3008
  - resource_requirements
2897
3009
  - bookable_online
3010
+ - payment
2898
3011
  - max_per_booking
2899
3012
  - color
2900
3013
  - sort_order
@@ -2928,6 +3041,13 @@ components:
2928
3041
  type: string
2929
3042
  bookable_online:
2930
3043
  type: boolean
3044
+ payment:
3045
+ description: >-
3046
+ Per-service payment requirement. `null` means no override — the
3047
+ workspace rule decides.
3048
+ oneOf:
3049
+ - $ref: "#/components/schemas/BookingPaymentMode"
3050
+ - type: "null"
2931
3051
  max_per_booking:
2932
3052
  type: [integer, "null"]
2933
3053
  color:
@@ -3148,6 +3268,7 @@ components:
3148
3268
  - party_sequence_id
3149
3269
  - amount_ore
3150
3270
  - payment_status
3271
+ - payment_mode
3151
3272
  - time_zone
3152
3273
  - cancel_window_hours
3153
3274
  - reschedule_window_hours
@@ -3195,6 +3316,11 @@ components:
3195
3316
  oneOf:
3196
3317
  - $ref: "#/components/schemas/BookingPaymentStatus"
3197
3318
  - type: "null"
3319
+ payment_mode:
3320
+ description: >-
3321
+ What this booking requires — what a manage page checks before
3322
+ offering «Betal nå».
3323
+ $ref: "#/components/schemas/BookingPaymentMode"
3198
3324
  time_zone:
3199
3325
  description: IANA zone the booking's local times should be rendered in.
3200
3326
  type: [string, "null"]
@@ -3259,6 +3385,131 @@ components:
3259
3385
  $ref: "#/components/schemas/BookingTimestampInput"
3260
3386
  new_resource_id:
3261
3387
  type: string
3388
+ StartBookingPaymentInput:
3389
+ description: >-
3390
+ Body for `startBookingPayment` and `startManageBookingPayment`.
3391
+ type: object
3392
+ required: [return_url, terms_accepted]
3393
+ properties:
3394
+ return_url:
3395
+ description: >-
3396
+ Where the wallet returns the customer. Must be a URL one of this
3397
+ workspace's own sites vouches for; anything else answers 422.
3398
+ type: string
3399
+ minLength: 1
3400
+ maxLength: 2048
3401
+ terms_accepted:
3402
+ description: >-
3403
+ Must be literally `true`. The customer has to actively accept the
3404
+ merchant's terms BEFORE a payment is initiated, so omitting it or
3405
+ sending `false` is a 400 and leaves no payment behind.
3406
+ const: true
3407
+ type: boolean
3408
+ terms_version:
3409
+ description: The caller's own version label for the terms that were accepted.
3410
+ type: string
3411
+ minLength: 1
3412
+ maxLength: 64
3413
+ terms_text:
3414
+ description: The exact text that was accepted, stored with the consent record.
3415
+ type: string
3416
+ maxLength: 2000
3417
+ BookingPaymentStart:
3418
+ description: >-
3419
+ The result of starting a payment. `redirect_url` is handed over here
3420
+ and nowhere else.
3421
+ type: object
3422
+ required: [reference, redirect_url, state]
3423
+ properties:
3424
+ reference:
3425
+ description: The wallet reference for this attempt.
3426
+ type: string
3427
+ redirect_url:
3428
+ description: >-
3429
+ Send the customer here UNCHANGED — hand it to the Vipps Widget SDK.
3430
+ SHOW-ONCE: it is not returned by `getBookingPayment`, and the
3431
+ payment behind it expires after ten minutes.
3432
+ type: string
3433
+ state:
3434
+ $ref: "#/components/schemas/BookingPaymentState"
3435
+ BookingPayment:
3436
+ description: >-
3437
+ One payment attempt on a booking. Money is integer øre, and the four
3438
+ aggregates are numbers rather than nulls — "nothing captured" is `0`.
3439
+ `redirect_url`, the wallet's own refusal text and the trace id are
3440
+ deliberately absent.
3441
+ type: object
3442
+ required:
3443
+ - reference
3444
+ - provider
3445
+ - state
3446
+ - mode
3447
+ - attempt
3448
+ - amount_ore
3449
+ - authorized_ore
3450
+ - captured_ore
3451
+ - refunded_ore
3452
+ - cancelled_ore
3453
+ - currency
3454
+ - capture_guaranteed_until
3455
+ - terms_version
3456
+ - terms_accepted_at
3457
+ - failure_code
3458
+ - created_at
3459
+ - updated_at
3460
+ properties:
3461
+ reference:
3462
+ type: string
3463
+ provider:
3464
+ const: vipps
3465
+ type: string
3466
+ state:
3467
+ $ref: "#/components/schemas/BookingPaymentState"
3468
+ mode:
3469
+ description: Never `none` — a payment exists only where one was required.
3470
+ type: string
3471
+ enum: [reserve, prepay]
3472
+ attempt:
3473
+ description: 1 for the first attempt on this booking, incrementing per retry.
3474
+ type: integer
3475
+ amount_ore:
3476
+ description: The amount this attempt is for, in integer øre.
3477
+ type: integer
3478
+ authorized_ore:
3479
+ type: integer
3480
+ captured_ore:
3481
+ type: integer
3482
+ refunded_ore:
3483
+ type: integer
3484
+ cancelled_ore:
3485
+ type: integer
3486
+ currency:
3487
+ const: NOK
3488
+ type: string
3489
+ capture_guaranteed_until:
3490
+ description: >-
3491
+ The last moment a capture is GUARANTEED to succeed. The card behind
3492
+ the wallet may release the reservation afterwards, so a capture
3493
+ past it can fail even though the payment still looks authorized.
3494
+ Null until the customer has approved.
3495
+ type: [string, "null"]
3496
+ format: date-time
3497
+ terms_version:
3498
+ type: [string, "null"]
3499
+ terms_accepted_at:
3500
+ type: [string, "null"]
3501
+ format: date-time
3502
+ failure_code:
3503
+ description: >-
3504
+ The wallet's numeric error code from the last failed operation —
3505
+ what a caller branches on.
3506
+ type: [string, "null"]
3507
+ created_at:
3508
+ type: [string, "null"]
3509
+ format: date-time
3510
+ updated_at:
3511
+ type: [string, "null"]
3512
+ format: date-time
3262
3513
  BookingsPagination:
3263
3514
  description: >-
3264
3515
  Pagination for a bookings page. `truncated` is the extra statement:
@@ -3981,6 +4232,10 @@ components:
3981
4232
  $ref: "#/components/schemas/Envelope_BookingActionResult"
3982
4233
  ApiResponse_BookingRescheduleResult:
3983
4234
  $ref: "#/components/schemas/Envelope_BookingRescheduleResult"
4235
+ ApiResponse_BookingPaymentStart:
4236
+ $ref: "#/components/schemas/Envelope_BookingPaymentStart"
4237
+ ApiResponse_BookingPayment:
4238
+ $ref: "#/components/schemas/Envelope_BookingPayment"
3984
4239
  ApiResponse_ManageSummary:
3985
4240
  $ref: "#/components/schemas/Envelope_ManageSummary"
3986
4241
  ApiResponse_PortalLoginStartResult:
@@ -4318,6 +4573,18 @@ components:
4318
4573
  properties:
4319
4574
  data:
4320
4575
  $ref: "#/components/schemas/BookingRescheduleResult"
4576
+ Envelope_BookingPaymentStart:
4577
+ type: object
4578
+ required: [data]
4579
+ properties:
4580
+ data:
4581
+ $ref: "#/components/schemas/BookingPaymentStart"
4582
+ Envelope_BookingPayment:
4583
+ type: object
4584
+ required: [data]
4585
+ properties:
4586
+ data:
4587
+ $ref: "#/components/schemas/BookingPayment"
4321
4588
  Envelope_ManageSummary:
4322
4589
  type: object
4323
4590
  required: [data]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@medalsocial/sdk",
3
- "version": "1.9.0",
3
+ "version": "1.10.0",
4
4
  "description": "TypeScript SDK for Medal Social API — posts, emails, contacts, deals, helpdesk, webhooks, and GDPR compliance",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Medal Social / Ali Aljumaili",
@@ -38,6 +38,8 @@ Errors throw `MedalApiError` (see the `client` skill for details).
38
38
  |---|---|---|
39
39
  | `medal.bookings` | `src/resources/bookings.ts` | `listServices(opts?)`, `listResources()`, `availability(opts)`, `schedule(opts)`, `list(opts?)`, `create(input, opts?)`, `get(id)`, `update(id, input, opts?)`, `cancel(id, input?, opts?)`, `reschedule(id, input, opts?)`, `markNoShow(id, opts?)` — all **staff** semantics (policy windows bypassed) |
40
40
  | `medal.bookings.manage` | `src/resources/bookings.ts` (`BookingsManage`) | `get(token)`, `cancel(token, input?, opts?)`, `reschedule(token, input, opts?)` — **customer** semantics (policy windows enforced) |
41
+ | `medal.bookings.payment` | `src/resources/bookings.ts` (`BookingsPayment`) | `start(id, input, opts?)`, `get(id)` — Vipps payment on a booking, as the business |
42
+ | `medal.bookings.manage.payment` | `src/resources/bookings.ts` (`BookingsManagePayment`) | `start(token, input, opts?)`, `get(token)` — the same two, on the customer's behalf |
41
43
  | `medal.bookings.persons` | `src/resources/bookings.ts` (`BookingsPersons`) | `list(contactId, { include_inactive? })`, `create(input)` — persons a contact books for (children, pets, employees) |
42
44
  | `medal.bookings.relations` | `src/resources/bookings.ts` (`BookingsRelations`) | `list(contactId)` → `{ outgoing, incoming }`, `create(input)` — directional relations between contacts |
43
45
  | `medal.bookings.events` | `src/resources/bookings.ts` (`BookingsEvents`) | `list({ from, to, status? })`, `get(id)`, `create(input)` — arrangementer (scheduled group sessions); registering a booking to an event ships in a later release |
@@ -378,6 +380,8 @@ The `with { type: "json" }` import-attribute syntax requires Node 24+ or a bundl
378
380
  | Hard-coded channel IDs in `posts.create` | Channels are workspace-specific | Call `posts.channels()` to discover them |
379
381
  | Looping on `next_cursor` alone for pagination | Can be non-null when `has_more: false` | Loop on `pagination.has_more` |
380
382
  | `medal.bookings.cancel(id)` to relay a customer's cancel | Bypasses the policy window and records `cancelled_by: 'staff'` | `medal.bookings.manage.cancel(token)` |
383
+ | Trusting the browser's return redirect after `payment.start` | The customer can close the tab or edit the URL; the outcome comes through Medal | Poll `medal.bookings.payment.get(id)`, or read `Booking.payment_status` |
384
+ | Caching `redirect_url` from `payment.start` | Show-once, and the payment expires in 10 minutes — a stale link leads nowhere | Start a new attempt |
381
385
  | Dividing `amount_ore` / `price_ore` into kroner for storage | Integer øre; a float rounds and the invoice is wrong | Keep the integer; format only at the point of display |
382
386
  | Reusing the old id or manage token after a reschedule | Reschedule inserts a NEW booking and mints a NEW token | Store `result.booking_id` and `result.manage_token` |
383
387
  | Expecting `manage_token` on an idempotent replay | Tokens are redacted from replayed responses | Persist it from the first response — a replay cannot give it back |