deliveryapi 0.1.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.
- package/README.md +252 -0
- package/dist/index.cjs +210 -0
- package/dist/index.d.cts +417 -0
- package/dist/index.d.ts +417 -0
- package/dist/index.js +204 -0
- package/package.json +52 -0
package/README.md
ADDED
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
# deliveryapi
|
|
2
|
+
|
|
3
|
+
Official JavaScript/TypeScript SDK for the [DeliveryAPI](https://deliveryapi.co.kr) — Korea's unified courier tracking API.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install deliveryapi
|
|
9
|
+
# or
|
|
10
|
+
yarn add deliveryapi
|
|
11
|
+
# or
|
|
12
|
+
pnpm add deliveryapi
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Quick Start
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import { DeliverySaasClient } from 'deliveryapi'
|
|
19
|
+
|
|
20
|
+
const client = new DeliverySaasClient({
|
|
21
|
+
apiKey: 'your-api-key',
|
|
22
|
+
secretKey: 'your-secret-key',
|
|
23
|
+
})
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
API keys can be issued at [deliveryapi.co.kr](https://deliveryapi.co.kr).
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## Tracking
|
|
31
|
+
|
|
32
|
+
### Single tracking
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
const result = await client.tracking.getOne('LOTTE', '1234567890')
|
|
36
|
+
|
|
37
|
+
console.log(result.results[0].status) // 'DELIVERED'
|
|
38
|
+
console.log(result.results[0].progresses) // delivery history
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Multiple tracking at once
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
const result = await client.tracking.get([
|
|
45
|
+
{ courierCode: 'LOTTE', trackingNumber: '1234567890' },
|
|
46
|
+
{ courierCode: 'CJ', trackingNumber: '9876543210' },
|
|
47
|
+
])
|
|
48
|
+
|
|
49
|
+
for (const item of result.results) {
|
|
50
|
+
console.log(item.trackingNumber, item.status)
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Without delivery history (faster)
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
const result = await client.tracking.getOne('CJ', '1234567890', {
|
|
58
|
+
includeProgresses: false,
|
|
59
|
+
})
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## Webhook Endpoints
|
|
65
|
+
|
|
66
|
+
Register a URL to receive real-time delivery status change notifications.
|
|
67
|
+
|
|
68
|
+
### Register endpoint
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
const endpoint = await client.webhooks.create({
|
|
72
|
+
url: 'https://your-server.com/webhook',
|
|
73
|
+
name: 'Production Webhook',
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
console.log(endpoint.id) // 'ep_xxxxxxxxxxxx'
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### List endpoints
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
const { endpoints } = await client.webhooks.list()
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Test endpoint
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
const result = await client.webhooks.test('ep_xxxxxxxxxxxx')
|
|
89
|
+
console.log(result.success) // true
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Delete endpoint
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
await client.webhooks.delete('ep_xxxxxxxxxxxx')
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## Tracking Subscriptions
|
|
101
|
+
|
|
102
|
+
Subscribe to a tracking number and receive webhook notifications when status changes.
|
|
103
|
+
|
|
104
|
+
### Create subscription
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
const subscription = await client.subscriptions.create({
|
|
108
|
+
courierCode: 'LOTTE',
|
|
109
|
+
trackingNumber: '1234567890',
|
|
110
|
+
endpointId: 'ep_xxxxxxxxxxxx',
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
console.log(subscription.id) // 'sub_xxxxxxxxxxxx'
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Subscribe to specific statuses only
|
|
117
|
+
|
|
118
|
+
```typescript
|
|
119
|
+
const subscription = await client.subscriptions.create({
|
|
120
|
+
courierCode: 'CJ',
|
|
121
|
+
trackingNumber: '9876543210',
|
|
122
|
+
endpointId: 'ep_xxxxxxxxxxxx',
|
|
123
|
+
subscribedStatuses: ['IN_TRANSIT', 'DELIVERED'],
|
|
124
|
+
})
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### List subscriptions
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
const { subscriptions, total } = await client.subscriptions.list({
|
|
131
|
+
status: 'ACTIVE',
|
|
132
|
+
page: 1,
|
|
133
|
+
pageSize: 20,
|
|
134
|
+
})
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Retrieve subscription
|
|
138
|
+
|
|
139
|
+
```typescript
|
|
140
|
+
const subscription = await client.subscriptions.retrieve('sub_xxxxxxxxxxxx')
|
|
141
|
+
console.log(subscription.status) // 'ACTIVE'
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Cancel subscription
|
|
145
|
+
|
|
146
|
+
```typescript
|
|
147
|
+
await client.subscriptions.cancel('sub_xxxxxxxxxxxx')
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## Couriers
|
|
153
|
+
|
|
154
|
+
### List supported couriers
|
|
155
|
+
|
|
156
|
+
```typescript
|
|
157
|
+
const { couriers } = await client.couriers.list()
|
|
158
|
+
|
|
159
|
+
for (const courier of couriers) {
|
|
160
|
+
console.log(courier.code, courier.name) // 'LOTTE', '롯데택배'
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## Webhook Payload
|
|
167
|
+
|
|
168
|
+
When a tracking status changes, your endpoint receives a POST request with the following payload:
|
|
169
|
+
|
|
170
|
+
```typescript
|
|
171
|
+
import type { WebhookPayload } from 'deliveryapi'
|
|
172
|
+
|
|
173
|
+
app.post('/webhook', (req, res) => {
|
|
174
|
+
const payload: WebhookPayload = req.body
|
|
175
|
+
|
|
176
|
+
console.log(payload.subscriptionId)
|
|
177
|
+
console.log(payload.courierCode)
|
|
178
|
+
console.log(payload.trackingNumber)
|
|
179
|
+
console.log(payload.status) // 'DELIVERED'
|
|
180
|
+
console.log(payload.changedAt) // ISO 8601 timestamp
|
|
181
|
+
|
|
182
|
+
res.sendStatus(200)
|
|
183
|
+
})
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
## Error Handling
|
|
189
|
+
|
|
190
|
+
```typescript
|
|
191
|
+
import {
|
|
192
|
+
DeliverySaasClient,
|
|
193
|
+
AuthenticationError,
|
|
194
|
+
RateLimitError,
|
|
195
|
+
NotFoundError,
|
|
196
|
+
DeliverySaasError,
|
|
197
|
+
} from 'deliveryapi'
|
|
198
|
+
|
|
199
|
+
try {
|
|
200
|
+
const result = await client.tracking.getOne('LOTTE', '1234567890')
|
|
201
|
+
} catch (error) {
|
|
202
|
+
if (error instanceof AuthenticationError) {
|
|
203
|
+
// Invalid API key or secret key
|
|
204
|
+
console.error('Authentication failed')
|
|
205
|
+
} else if (error instanceof RateLimitError) {
|
|
206
|
+
// Request limit exceeded
|
|
207
|
+
console.error('Rate limit exceeded, retry later')
|
|
208
|
+
} else if (error instanceof NotFoundError) {
|
|
209
|
+
// Resource not found
|
|
210
|
+
console.error('Not found')
|
|
211
|
+
} else if (error instanceof DeliverySaasError) {
|
|
212
|
+
// Other API errors
|
|
213
|
+
console.error(error.message, error.statusCode)
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
---
|
|
219
|
+
|
|
220
|
+
## TypeScript
|
|
221
|
+
|
|
222
|
+
Full TypeScript support is included out of the box.
|
|
223
|
+
|
|
224
|
+
```typescript
|
|
225
|
+
import type {
|
|
226
|
+
TrackingResponse,
|
|
227
|
+
TrackingSubscription,
|
|
228
|
+
WebhookEndpoint,
|
|
229
|
+
WebhookPayload,
|
|
230
|
+
CourierDeliveryStatus,
|
|
231
|
+
} from 'deliveryapi'
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
---
|
|
235
|
+
|
|
236
|
+
## Supported Couriers
|
|
237
|
+
|
|
238
|
+
| Code | Name |
|
|
239
|
+
|------|------|
|
|
240
|
+
| `LOTTE` | 롯데택배 |
|
|
241
|
+
| `CJ` | CJ대한통운 |
|
|
242
|
+
| `HANJIN` | 한진택배 |
|
|
243
|
+
| `POST` | 우체국택배 |
|
|
244
|
+
| `LOGEN` | 로젠택배 |
|
|
245
|
+
|
|
246
|
+
Full list available via `client.couriers.list()`.
|
|
247
|
+
|
|
248
|
+
---
|
|
249
|
+
|
|
250
|
+
## License
|
|
251
|
+
|
|
252
|
+
MIT
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/errors.ts
|
|
4
|
+
var DeliverySaasError = class extends Error {
|
|
5
|
+
constructor(message, statusCode, code) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.statusCode = statusCode;
|
|
8
|
+
this.code = code;
|
|
9
|
+
this.name = "DeliverySaasError";
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
var AuthenticationError = class extends DeliverySaasError {
|
|
13
|
+
constructor(message = "Invalid API key or secret key") {
|
|
14
|
+
super(message, 401, "AUTHENTICATION_ERROR");
|
|
15
|
+
this.name = "AuthenticationError";
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
var RateLimitError = class extends DeliverySaasError {
|
|
19
|
+
constructor(message = "Rate limit exceeded") {
|
|
20
|
+
super(message, 429, "RATE_LIMIT_ERROR");
|
|
21
|
+
this.name = "RateLimitError";
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
var NotFoundError = class extends DeliverySaasError {
|
|
25
|
+
constructor(message = "Resource not found") {
|
|
26
|
+
super(message, 404, "NOT_FOUND");
|
|
27
|
+
this.name = "NotFoundError";
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
// src/http.ts
|
|
32
|
+
var HttpClient = class {
|
|
33
|
+
constructor(apiKey, secretKey, baseUrl) {
|
|
34
|
+
this.baseUrl = baseUrl.replace(/\/$/, "");
|
|
35
|
+
this.authHeader = `Bearer ${apiKey}:${secretKey}`;
|
|
36
|
+
}
|
|
37
|
+
async get(path, params) {
|
|
38
|
+
const url = new URL(`${this.baseUrl}${path}`);
|
|
39
|
+
if (params) {
|
|
40
|
+
Object.entries(params).forEach(([key, value]) => url.searchParams.set(key, value));
|
|
41
|
+
}
|
|
42
|
+
return this.request("GET", url.toString());
|
|
43
|
+
}
|
|
44
|
+
async post(path, body) {
|
|
45
|
+
return this.request("POST", `${this.baseUrl}${path}`, body);
|
|
46
|
+
}
|
|
47
|
+
async patch(path, body) {
|
|
48
|
+
return this.request("PATCH", `${this.baseUrl}${path}`, body);
|
|
49
|
+
}
|
|
50
|
+
async delete(path) {
|
|
51
|
+
return this.request("DELETE", `${this.baseUrl}${path}`);
|
|
52
|
+
}
|
|
53
|
+
async request(method, url, body) {
|
|
54
|
+
const headers = {
|
|
55
|
+
Authorization: this.authHeader,
|
|
56
|
+
"Content-Type": "application/json"
|
|
57
|
+
};
|
|
58
|
+
const response = await fetch(url, {
|
|
59
|
+
method,
|
|
60
|
+
headers,
|
|
61
|
+
body: body !== void 0 ? JSON.stringify(body) : void 0
|
|
62
|
+
});
|
|
63
|
+
const json = await response.json();
|
|
64
|
+
if (!response.ok) {
|
|
65
|
+
if (response.status === 401) throw new AuthenticationError(json.message);
|
|
66
|
+
if (response.status === 404) throw new NotFoundError(json.message);
|
|
67
|
+
if (response.status === 429) throw new RateLimitError(json.message);
|
|
68
|
+
throw new DeliverySaasError(json.message ?? "Request failed", response.status, json.error);
|
|
69
|
+
}
|
|
70
|
+
if (!json.isSuccess) {
|
|
71
|
+
throw new DeliverySaasError(json.message ?? "API returned failure", response.status, json.error);
|
|
72
|
+
}
|
|
73
|
+
return json.data;
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
// src/resources/tracking.ts
|
|
78
|
+
var TrackingResource = class {
|
|
79
|
+
constructor(http) {
|
|
80
|
+
this.http = http;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* 택배 조회 (공개 API, 계정 불필요)
|
|
84
|
+
* @param items 조회할 택배 목록 (1건도 배열로)
|
|
85
|
+
* @param includeProgresses 진행 내역 포함 여부 (기본값: true)
|
|
86
|
+
*/
|
|
87
|
+
async get(items, includeProgresses = true) {
|
|
88
|
+
return this.http.post("/v1/courier/track", {
|
|
89
|
+
items,
|
|
90
|
+
includeProgresses
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* 단건 택배 조회 (편의 메서드)
|
|
95
|
+
*/
|
|
96
|
+
async getOne(courierCode, trackingNumber, options) {
|
|
97
|
+
return this.get(
|
|
98
|
+
[{ courierCode, trackingNumber, clientId: options?.clientId }],
|
|
99
|
+
options?.includeProgresses ?? true
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
// src/resources/subscriptions.ts
|
|
105
|
+
var SubscriptionsResource = class {
|
|
106
|
+
constructor(http) {
|
|
107
|
+
this.http = http;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* 택배 구독 생성
|
|
111
|
+
* 상태 변경 시 등록된 웹훅 엔드포인트로 알림 전송
|
|
112
|
+
*/
|
|
113
|
+
async create(data) {
|
|
114
|
+
return this.http.post("/v1/tracking/subscriptions", data);
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* 택배 구독 목록 조회
|
|
118
|
+
*/
|
|
119
|
+
async list(options) {
|
|
120
|
+
const params = {};
|
|
121
|
+
if (options?.status) params["status"] = options.status;
|
|
122
|
+
if (options?.page) params["page"] = String(options.page);
|
|
123
|
+
if (options?.pageSize) params["pageSize"] = String(options.pageSize);
|
|
124
|
+
return this.http.get("/v1/tracking/subscriptions", params);
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* 택배 구독 상세 조회
|
|
128
|
+
*/
|
|
129
|
+
async retrieve(subscriptionId) {
|
|
130
|
+
return this.http.get(`/v1/tracking/subscriptions/${subscriptionId}`);
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* 택배 구독 취소
|
|
134
|
+
*/
|
|
135
|
+
async cancel(subscriptionId) {
|
|
136
|
+
await this.http.delete(`/v1/tracking/subscriptions/${subscriptionId}`);
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
// src/resources/webhooks.ts
|
|
141
|
+
var WebhooksResource = class {
|
|
142
|
+
constructor(http) {
|
|
143
|
+
this.http = http;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* 웹훅 엔드포인트 등록
|
|
147
|
+
*/
|
|
148
|
+
async create(data) {
|
|
149
|
+
return this.http.post("/v1/webhook-v2/endpoints", data);
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* 웹훅 엔드포인트 목록 조회
|
|
153
|
+
*/
|
|
154
|
+
async list() {
|
|
155
|
+
return this.http.get("/v1/webhook-v2/endpoints");
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* 웹훅 엔드포인트 상세 조회
|
|
159
|
+
*/
|
|
160
|
+
async retrieve(endpointId) {
|
|
161
|
+
return this.http.get(`/v1/webhook-v2/endpoints/${endpointId}`);
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* 웹훅 엔드포인트 삭제
|
|
165
|
+
*/
|
|
166
|
+
async delete(endpointId) {
|
|
167
|
+
await this.http.delete(`/v1/webhook-v2/endpoints/${endpointId}`);
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* 웹훅 엔드포인트 테스트 전송
|
|
171
|
+
*/
|
|
172
|
+
async test(endpointId) {
|
|
173
|
+
return this.http.post(`/v1/webhook-v2/endpoints/${endpointId}/test`);
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
// src/resources/couriers.ts
|
|
178
|
+
var CouriersResource = class {
|
|
179
|
+
constructor(http) {
|
|
180
|
+
this.http = http;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* 지원 택배사 목록 조회
|
|
184
|
+
*/
|
|
185
|
+
async list() {
|
|
186
|
+
return this.http.get("/v1/courier/list");
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
// src/client.ts
|
|
191
|
+
var DEFAULT_BASE_URL = "https://api.deliveryapi.co.kr";
|
|
192
|
+
var DeliverySaasClient = class {
|
|
193
|
+
constructor(config) {
|
|
194
|
+
const http = new HttpClient(
|
|
195
|
+
config.apiKey,
|
|
196
|
+
config.secretKey,
|
|
197
|
+
config.baseUrl ?? DEFAULT_BASE_URL
|
|
198
|
+
);
|
|
199
|
+
this.tracking = new TrackingResource(http);
|
|
200
|
+
this.subscriptions = new SubscriptionsResource(http);
|
|
201
|
+
this.webhooks = new WebhooksResource(http);
|
|
202
|
+
this.couriers = new CouriersResource(http);
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
exports.AuthenticationError = AuthenticationError;
|
|
207
|
+
exports.DeliverySaasClient = DeliverySaasClient;
|
|
208
|
+
exports.DeliverySaasError = DeliverySaasError;
|
|
209
|
+
exports.NotFoundError = NotFoundError;
|
|
210
|
+
exports.RateLimitError = RateLimitError;
|