Chuyển tới nội dung chính
Tác giả
Lương Thanh Hoàn
Lương Thanh HoànSoftware Engineering Student

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

  1. Bối cảnh & Nguyên nhân gốc rễ
  2. Phần 1: Tạo App Store Connect API Key
  3. Phần 2: Tạo Distribution Certificate thủ công
  4. Phần 3: Tạo Provisioning Profile thủ công
  5. Phần 4: Cấu hình credentials.json & eas.json
  6. Phần 5: Build
  7. Phần 6: Submit lên TestFlight
  8. Checklist trước submit App Store
  9. 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:

  1. Vào appstoreconnect.apple.com/access/api → tab IntegrationsApp Store Connect APIKeys
  2. Bấm Generate API Key, đặt tên (vd EAS-Build), chọn role App Manager hoặc Admin
  3. ⚠️ Tải file .p8 về ngay (chỉ tải được 1 lần duy nhất, mất phải revoke tạo lại)
  4. Ghi lại Key IDIssuer ID hiển thị trên trang

Gán key vào project qua CLI

eas credentials

Chọn:

  • iOSApp Store Connect: Manage your API KeyAdd 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 (vd D:\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 Applebỏ 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ề

  1. Vào developer.apple.com (đăng nhập trình duyệt bình thường, KHÔNG qua CLI)
  2. Certificates, Identifiers & ProfilesCertificates+
  3. Chọn Apple Distribution
  4. Upload distribution.csr
  5. Tải file .cer về

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 -legacy khiế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:

  • iOSBuild CredentialsDistribution 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/accountMembership details (chuỗi 10 ký tự, vd 3LC2J9383Z)

📝 Lưu ý:

  • Nếu thấy Unable to validate distribution certificate, you are not authenticated with Applevẫ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:

  1. Chọn loại DistributionApp Store Connect (dùng để submit lên TestFlight/App Store)
  2. Chọn App ID: com.qranty.admin (nếu chưa có, tạo trước ở Identifiers+App ID → nhập bundle ID)
  3. Chọn Certificate: chọn đúng Distribution Certificate vừa tạo ở Phần 2 (theo Team ID 3LC2J9383Z)
  4. Đặt tên profile, bấm Generate
  5. Tải file .mobileprovision về, đặt tên gọn (vd Qranty_Profile.mobileprovision)
  6. 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.mobileprovisiondistribution.p12 nằm đúng trong thư mục root project
  • ✅ Khớp tên với path khai ở trên
  • Thêm credentials.json và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/...
  • ascAppId6789801450

🔐 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ừ queuedbuildingfinished

Phần 6: Submit lên TestFlight

Chạy lệnh submit

eas submit --platform ios --profile production

Các bước:

  1. Chọn Select a build from EAS
  2. Chọn build mới nhất, status finished
  3. Đợ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:

  1. Vào App Store Connect → tab TestFlight
  2. Build ở trạng thái Processing (10 phút – vài giờ)
  3. 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)

  1. App Store Connect → TestFlightInternal Testing
  2. Tạo group test
  3. Thêm email Apple ID của người test (tối đa 100 người)
  4. Gắn build vào group
  5. Người được mời cài app TestFlight trên iPhone
  6. Đă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 traTrạ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ảiNguyên nhânGiải pháp
SMS 2FA "Verification codes can't be sent"Lỗi server Apple khi login CLIDùng ASC API Key thay Apple ID/password
Certificate "hasn't been imported successfully"OpenSSL 3.x thiếu flag -legacy khi export .p12Luô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 KeyTạo profile thủ công qua web, dùng credentials.json local
eas submit hỏi login Apple IDThiếu ascAppId trong eas.json, EAS phải tự tìm appKhai 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