프로그래밍 방식 인코딩 — Ultra 구독자 전용. 계정 → API 키에서 키를 생성하고, 키를 그대로 아래 엔드포인트 URL에 포함시키면 됩니다.
| 플랜 | 파일당 상한 | API 액세스 | multipart 필요? |
|---|---|---|---|
| Free | 5 KB | — | — |
| Pro | 100 MB | — | 아니오 (단일 PUT 가능) |
| Ultra | 300 MB | 있음 | 파일 >100 MB는 multipart 필수 |
Cloudflare 엣지는 단일 request body를 100 MB로 제한합니다. 이 값을 초과하는 파일은 50 MB part로 나누어 아래 multipart 엔드포인트를 사용하세요.
POST https://webhook.binaryphp.com/upload/<api_key>
X-BPHP-Filename: plugin.php # 필수, 확장자는 .php 또는 .zip
X-BPHP-Domain: app.example.com,*.example.com
X-BPHP-Mac: aa:bb:cc:dd:ee:ff # 선택
X-BPHP-Expire: 2027-12-31 # 선택
X-BPHP-Webhook: https://you.example.com/wh # 선택, 비동기 알림용
<파일 바이너리를 request body로>
→ 200 {
"job_id": "...",
"download_url": "https://...presigned R2 URL...",
"expires_at": "2026-05-09T...",
"size": 12345,
"license": { "domains": [...], ... }
}
curl 한 줄 버전:
curl -X POST https://webhook.binaryphp.com/upload/$API_KEY \
-H "X-BPHP-Filename: plugin.php" \
-H "X-BPHP-Domain: app.example.com" \
--data-binary @plugin.php
3 단계: init → 각 part를 개별 PUT → complete.
POST https://webhook.binaryphp.com/upload/<api_key>/init
X-BPHP-Filename: bigplugin.zip
X-BPHP-Total-Size: 200000000
→ 200 {
"job_id": "...",
"upload_id": "...", # R2 multipart 불투명 id
"r2_key": "uploads/<job_id>.zip",
"part_size": 52428800, # 권장 50 MB / part
"part_count": 4,
"max_part_size": 104857600, # 100 MB 하드 리미트
"parts": [
{ "part_no": 1, "upload_url": "https://...presigned PUT..." },
{ "part_no": 2, "upload_url": "..." },
{ "part_no": 3, "upload_url": "..." },
{ "part_no": 4, "upload_url": "..." }
]
}
upload_url에 PUT이 presigned URL들은 직접 R2로 전송되어 Worker 대역폭을 소비하지 않습니다.
각 PUT 응답의 ETag 헤더는 수집하여, complete
단계에서 함께 전송하세요:
PUT <upload_url>
<파일의 (part_no-1)*part_size 오프셋에서 part_size 바이트를 읽음>
← 200 OK
ETag: "abc123def456..."
각 part는 ≥5 MB(마지막 part만 더 작아도 됨), 그리고 ≤100 MB.
POST https://webhook.binaryphp.com/upload/<api_key>/complete
Content-Type: application/json
{
"upload_id": "...",
"r2_key": "uploads/<job_id>.zip",
"filename": "bigplugin.zip",
"parts": [
{ "part_no": 1, "etag": "\"abc...\"" },
{ "part_no": 2, "etag": "\"def...\"" },
{ "part_no": 3, "etag": "\"ghi...\"" },
{ "part_no": 4, "etag": "\"jkl...\"" }
],
"domain": "app.example.com",
"mac": "",
"expire": "2027-12-31",
"plugin": "",
"webhook": ""
}
→ 200 { "job_id", "download_url", "expires_at", "size", "license" }
중간에 part 업로드가 실패한 경우, /upload/<api_key>/abort를
{ upload_id, r2_key }와 함께 호출하여 R2에 남은 part가 계속 과금되는 것을 방지하세요.
X-BPHP-Webhook(또는 complete body의 "webhook")을
설정하면 encode.completed 이벤트를 수신할 수 있습니다. 알림은 Cloudflare 엣지 IP에서 발송되어
우리 호스트 IP가 노출되지 않습니다. Body는 당신의 API key를 공유 비밀로 사용해 HMAC-SHA256으로 서명됩니다:
POST <당신의 webhook URL>
X-BinaryPHP-Signature: t=1736208000,v1=<hex>
X-BinaryPHP-Event: encode.completed
Content-Type: application/json
{
"event": "encode.completed",
"job_id": "...",
"download_url": "...",
"expires_at": "...",
"size": 12345,
"license": { ... }
}
검증 예시 (PHP):
$sig_h = $_SERVER['HTTP_X_BINARYPHP_SIGNATURE'] ?? '';
[$tpart, $vpart] = array_pad(explode(',', $sig_h, 2), 2, '');
$ts = (int) substr($tpart, 2);
$got = substr($vpart, 3);
if (abs(time() - $ts) > 300) http_response_code(401);
$body = file_get_contents('php://input');
$expected = hash_hmac('sha256', "$ts.$body", $YOUR_API_KEY);
if (!hash_equals($expected, $got)) http_response_code(401);
ext-curl만 필요. 파일 크기에 따라 단일 또는 multipart 플로우 자동 선택;
중간 실패 시 R2 multipart를 abort해 잔여 part로 인한 과금을 방지합니다.
API_KEY=bphp_live_xxx php upload.php big.zip --domain=app.example.com
requests만 필요. 마찬가지로 자동 판정 + abort 정리.
| 상태 코드 | 의미 | 처리 방법 |
|---|---|---|
| 401 | 키 오류 또는 폐기됨 | 「계정 → API 키」에서 재생성 |
| 402 | 플랜이 이 작업 미지원 (예: Free에서 zip 업로드) | 플랜 업그레이드 |
| 413 | 파일 / Content-Length가 플랜 상한 초과 | multipart로 전환, 또는 Pro→Ultra로 업그레이드 (300 MB) |
| 400 | 필드 누락 또는 형식 오류 (filename, parts 등) | 응답 body 확인, 누락된 필드가 표시됩니다 |
| 502 | R2 multipart 작업 실패 | init 재시도; R2는 가끔 <1% 확률로 실패할 수 있습니다 |