Quy trình Build & Submit iOS cho Qranty qua EAS
Ghi lại toàn bộ quá trình thực tế đã làm để build thành công file .ipa production và submit lên App Store Connect/TestFlight, sau khi gặp hàng loạt lỗi liên quan Apple 2FA/SMS và certificate.
Version: 1.0
Ngày cập nhật: 22/7/2026
Người cập nhật: Lương Thanh Hoàn
⚠️ Lưu ý quan trọng: Dùng file này để build lại lần sau hoặc hướng dẫn người khác trong team, không phải dò lại từ đầu.
Mục lục
- Bối cảnh & Nguyên nhân gốc rễ
- Phần 1: Tạo App Store Connect API Key
- Phần 2: Tạo Distribution Certificate thủ công
- Phần 3: Tạo Provisioning Profile thủ công
- Phần 4: Cấu hình credentials.json & eas.json
- Phần 5: Build
- Phần 6: Submit lên TestFlight
- Checklist trước submit App Store
- Tóm tắt & Giải pháp
Bối cảnh & Nguyên nhân gốc rễ
Khi build bằng eas build --platform ios --profile production và để EAS tự động quản lý credentials (đăng nhập Apple ID tương tác qua CLI), gặp 2 lỗi chính:
❌ Lỗi 1: Apple 2FA SMS
Verification codes can't be sent to this phone number at this time
Nguyên nhân: Lỗi từ server Apple (không phải lỗi cấu hình), do CLI login yêu cầu xác thực SMS 2FA và Apple đôi khi chặn/nghẽn gửi SMS tới tài khoản Apple Developer dùng chung công ty.
❌ Lỗi 2: Certificate không tương thích
Distribution certificate ... hasn't been imported successfully
Nguyên nhân: Certificate .p12 tự tạo bằng OpenSSL trên Windows dùng cipher mặc định của OpenSSL 3.x (AES-256), không tương thích với công cụ ký của EAS build server (kỳ vọng cipher "legacy" RC2/3DES).
✅ Giải pháp tổng thể:
Bỏ hoàn toàn việc login Apple ID tương tác qua CLI, chuyển sang:
- Dùng App Store Connect (ASC) API Key thay cho Apple ID + password khi submit
- Tự tạo certificate + provisioning profile thủ công qua trình duyệt (không qua CLI), rồi trỏ EAS đọc từ file local (
credentials.json,credentialsSource: "local")
Phần 1: Tạo App Store Connect API Key
Vì sao cần?
🔑 API Key dùng để xác thực với Apple mà không cần password + SMS 2FA, dùng được cho cả build lẫn submit.
Các bước thực hiện:
- Vào
appstoreconnect.apple.com/access/api→ tab Integrations → App Store Connect API → Keys - Bấm Generate API Key, đặt tên (vd
EAS-Build), chọn role App Manager hoặc Admin - ⚠️ Tải file
.p8về ngay (chỉ tải được 1 lần duy nhất, mất phải revoke tạo lại) - Ghi lại Key ID và Issuer ID hiển thị trên trang
Gán key vào project qua CLI
eas credentials
Chọn:
- iOS → App Store Connect: Manage your API Key → Add a new API Key For EAS Submit
- Khi hỏi "Generate a new App Store Connect API Key?" chọn No (chọn Yes sẽ lại bắt login Apple ID qua CLI)
Điền thông tin:
- Path to App Store Connect API Key: đường dẫn file
.p8(vdD:\Qranty\api_key.p8) - Key ID: (từ bước trên)
- Issuer ID: (từ bước trên)
📝 Lưu ý:
- Nếu thấy dòng
Unable to validate App Store Connect API Key, you are not authenticated with Apple→ bỏ qua, đây chỉ là bước validate phụ tùy chọn- Key vẫn được lưu và dùng được bình thường
- Xác nhận Yes khi được hỏi gán key cho project
⚠️ Quan trọng: Gán key ở bước này chỉ áp dụng cho EAS Submit, chưa áp dụng cho build credentials (certificate/provisioning profile) — hai phần đó xử lý riêng ở Phần 2 & 3
Phần 2: Tạo Distribution Certificate thủ công
Vì sao cần?
🚫 Tránh bước eas credentials → "All: Set up all..." hoặc "Generate a new Apple Distribution Certificate? → Yes", vì cả hai đều ép login Apple ID qua CLI → dính lỗi SMS 2FA.
Bước 1 — Tạo CSR (Certificate Signing Request) trên Windows
openssl genrsa -out distribution.key 2048
openssl req -new -key distribution.key -out distribution.csr -subj "/emailAddress=<apple-id-email>, CN=Qranty Distribution, C=VN"
Bước 2 — Upload CSR lên Apple, tải certificate về
- Vào
developer.apple.com(đăng nhập trình duyệt bình thường, KHÔNG qua CLI) - Certificates, Identifiers & Profiles → Certificates → +
- Chọn Apple Distribution
- Upload
distribution.csr - Tải file
.cervề
Bước 3 — Convert certificate sang .pem
openssl x509 -in distribution.cer -inform DER -out distribution.pem -outform PEM
Bước 4 — Export ra .p12 (🔴 BẮT BUỘC thêm flag -legacy)
openssl pkcs12 -export -legacy -inkey distribution.key -in distribution.pem -out distribution.p12 -passout pass:<mật khẩu tự đặt>
⚠️ Đây là nguyên nhân lỗi certificate lần đầu:
- Thiếu
-legacykhiến OpenSSL 3.x dùng cipher AES-256 mặc định- Không tương thích công cụ ký của EAS build server
- Luôn nhớ thêm flag này khi tạo lại certificate sau này
Bước 5 — Upload certificate vào EAS
eas credentials
Chọn:
- iOS → Build Credentials → Distribution Certificate: Add a new one to your account
- Khi hỏi "Generate a new Apple Distribution Certificate?" chọn No
Điền thông tin:
- Path to P12 file:
distribution.p12 - P12 password: đúng mật khẩu đã đặt ở Bước 4
- Apple Team ID: lấy tại
developer.apple.com/account→ Membership details (chuỗi 10 ký tự, vd3LC2J9383Z)
📝 Lưu ý:
- Nếu thấy
Unable to validate distribution certificate, you are not authenticated with Apple→ vẫn bỏ qua- Certificate đã được tạo và lưu thành công phía sau dòng đó (
✔ Created distribution certificate)- Xác nhận Yes khi được hỏi gán certificate cho project
Phần 3: Tạo Provisioning Profile thủ công
Vì sao cần?
📱 Bước "Provisioning Profile → Add a new one" trong eas credentials vẫn ép login CLI → phải tạo tay qua web rồi nộp file cho EAS đọc qua credentials.json local.
Lưu ý: Apple chưa hỗ trợ tạo Provisioning Profile qua ASC API Key trong flow này
Bước 1 — Tạo profile trên web
Vào developer.apple.com/account/resources/profiles/add:
- Chọn loại Distribution → App Store Connect (dùng để submit lên TestFlight/App Store)
- Chọn App ID:
com.qranty.admin(nếu chưa có, tạo trước ở Identifiers → + → App ID → nhập bundle ID) - Chọn Certificate: chọn đúng Distribution Certificate vừa tạo ở Phần 2 (theo Team ID
3LC2J9383Z) - Đặt tên profile, bấm Generate
- Tải file
.mobileprovisionvề, đặt tên gọn (vdQranty_Profile.mobileprovision) - Copy vào thư mục root project
Bước 2 — Không dùng menu tương tác của eas credentials cho bước này
❌ Tránh menu hỏi login
✅ Tạo trực tiếp file credentials.json (Phần 4)
Phần 4: Cấu hình credentials.json & eas.json
📄 File credentials.json (đặt ở root project)
{
"ios": {
"provisioningProfilePath": "Qranty_Profile.mobileprovision",
"distributionCertificate": {
"path": "distribution.p12",
"password": "<mật khẩu thật đã đặt ở Phần 2 Bước 4>"
}
}
}
⚠️ Lưu ý quan trọng:
- ✅ Đảm bảo cả 2 file
Qranty_Profile.mobileprovisionvàdistribution.p12nằm đúng trong thư mục root project- ✅ Khớp tên với path khai ở trên
- ❌ Thêm
credentials.jsonvào.gitignore(chứa private key)
📋 File eas.json — thêm credentialsSource: "local" cho profile production
{
"build": {
"production": {
"ios": {
"credentialsSource": "local"
},
"android": {
"buildType": "app-bundle"
},
"env": {
"EXPO_PUBLIC_API_URL": "https://staging-api.qranty.com"
}
}
}
}
📋 File eas.json — thêm ascAppId vào phần submit
{
"submit": {
"production": {
"ios": {
"ascAppId": "6789801450"
},
"android": {
"serviceAccountKeyPath": "./play-store-key.json",
"track": "internal"
}
}
}
}
Cách lấy ascAppId:
- Vào App Store Connect, tìm app của bạn
- URL sẽ là:
appstoreconnect.apple.com/apps/6789801450/... ascAppIdlà6789801450
🔐 Bảo mật — thêm vào .gitignore
credentials.json
distribution.p12
distribution.key
distribution.pem
api_key.p8
⚠️ Tuyệt đối không commit lên git — tất cả chứa private key
Phần 5: Build
Chạy lệnh build
eas build --platform ios --profile production
Nguyên lý hoạt động:
✅ Với credentialsSource: "local", EAS đọc trực tiếp .p12 + .mobileprovision từ máy
✅ Không gọi API validate với Apple
✅ Không còn bị chặn bởi 2FA/SMS
Theo dõi tiến trình:
- Qua URL build in ra trong terminal
- Hoặc
expo.dev→ project → Builds - Status chuyển từ
queued→building→finished
Phần 6: Submit lên TestFlight
Chạy lệnh submit
eas submit --platform ios --profile production
Các bước:
- Chọn Select a build from EAS
- Chọn build mới nhất, status
finished - Đợi upload hoàn tất
🎯 Lợi ích:
✅ Với ascAppId đã khai trong eas.json, bước "Ensuring your app exists on App Store Connect" được bỏ qua
✅ Không hỏi Apple ID nữa
📱 Sau khi upload xong:
- Vào App Store Connect → tab TestFlight
- Build ở trạng thái Processing (10 phút – vài giờ)
- Icon có thể hiện placeholder trống — bình thường, sẽ tự hiện icon thật sau khi Processing xong
⚠️ Lưu ý quan trọng:
- Distribution → Add for Review = Nộp app công khai lên App Store (Apple review, mất vài ngày)
- TestFlight Internal Testing = Cho test nội bộ (không cần Apple review)
- Khác hoàn toàn — không bấm "Add for Review" khi chỉ muốn test nội bộ
Thêm người test (không cần Apple review)
- App Store Connect → TestFlight → Internal Testing
- Tạo group test
- Thêm email Apple ID của người test (tối đa 100 người)
- Gắn build vào group
- Người được mời cài app TestFlight trên iPhone
- Đăng nhập đúng Apple ID được mời, tải bản test
Checklist trước submit App Store
Trước khi bấm "Add for Review" để submit lên App Store công khai (khác với TestFlight):
| Hạng mục kiểm tra | Trạng thái |
|---|---|
Đổi EXPO_PUBLIC_API_URL từ staging sang production | ☐ |
| Build lại với production API | ☐ |
Kiểm tra ./assets/adaptive-icon.png (1024x1024) | ☐ |
| Icon không có alpha channel (không trong suốt) | ☐ |
| Điền đầy đủ metadata (mô tả, keywords, support URL) | ☐ |
| Test kỹ qua TestFlight | ☐ |
| Xác nhận ổn định trước khi submit | ☐ |
🎨 Lưu ý icon: PNG không có alpha channel là lỗi phổ biến gây icon trống hoặc Apple reject
Tóm tắt & Giải pháp
📊 Bảng so sánh lỗi — nguyên nhân — giải pháp
| Lỗi gặp phải | Nguyên nhân | Giải pháp |
|---|---|---|
| SMS 2FA "Verification codes can't be sent" | Lỗi server Apple khi login CLI | Dùng ASC API Key thay Apple ID/password |
| Certificate "hasn't been imported successfully" | OpenSSL 3.x thiếu flag -legacy khi export .p12 | Luôn thêm -legacy khi export p12 |
eas credentials cứ hỏi login Apple ID (Provisioning Profile) | Apple chưa hỗ trợ tạo Provisioning Profile qua ASC API Key | Tạo profile thủ công qua web, dùng credentials.json local |
eas submit hỏi login Apple ID | Thiếu ascAppId trong eas.json, EAS phải tự tìm app | Khai sẵn ascAppId lấy từ URL App Store Connect |
Cập nhật lần cuối: 22/7/2026
Người cập nhật: Lương Thanh Hoàn