Puede que te sirva algo como esto :
Y el codigo es este :
LOCAL aKysFlgX := ObtPaySgnt( 1, nXKeyPl, oKysGrnd )
LOCAL aKysPayX := ObtPaySgnt( 2, nXKeyPl, oKysGrnd )
// 1 Corto 2 Largo
// nXKeyPl = Cuantos codigos a generar
// oKysGrnd = Lo puedes ignorar, es un SAY
Static Function ObtPaySgnt( nMode, nPayPlz, oKysGrnd )
LOCAL nKyGntSld, nKyStrSld, aXGrpKys, aXRekKys, aKysGnrtd := {}, cStrKyGnrd, aKys2Ret := {}, aKyGntrDts := { ;
{80,55,75,71,74,79,89,72,54,76,56,77,52,83,65,53,78,86,84,69,51,48,73,88,66,82,67,87,85,68,49,50,81,57,70}, ;
{51,69,67,52,77,48,85,54,70,74,65,75,53,50,84,79,68,86,66,80,72,57,76,81,89,55,88,49,73,87,56,83,82,71,78}, ;
{67,86,73,69,89,65,66,81,85,74,71,54,76,77,87,57,56,68,82,75,80,83,55,72,48,53,79,49,51,84,52,78,70,50,88}, ;
{51,80,79,78,76,70,48,86,56,49,87,52,84,77,73,54,85,88,81,75,57,74,82,66,72,69,71,67,83,53,65,55,50,68,89}, ;
{74,89,70,55,86,78,50,57,71,80,81,73,53,72,54,77,87,49,85,75,88,79,67,76,69,84,56,68,82,52,66,65,48,83,51} }
WHILE .T.
nKyGntSld := nRandom( Len( aKyGntrDts ) )
IF nKyGntSld == 0
SysWait(.5)
ELSE
IF aScan( aKysGnrtd, nKyGntSld ) == 0
AaDd( aKysGnrtd, nKyGntSld )
aXGrpKys := aKyGntrDts[nKyGntSld]
aXRekKys := {}
cStrKyGnrd := ""
WHILE .T.
nKyStrSld := nRandom( Len( aXGrpKys ) )
IF nKyStrSld == 0
SysWait(.5)
ELSE
IF aScan( aXRekKys, nKyStrSld ) == 0
AaDd( aXRekKys, nKyStrSld )
cStrKyGnrd += Chr( aXGrpKys[nKyStrSld] )
IF nMode == 1
cStrKyGnrd += IIF( Len( cStrKyGnrd ) == 3, "-", "" )
ELSE
IF Len( cStrKyGnrd ) == 3 .OR. Len( cStrKyGnrd ) == 6 .OR. Len( cStrKyGnrd ) == 11
cStrKyGnrd += "-"
ENDIF
ENDIF
IF Len( cStrKyGnrd ) == IIF( nMode == 1, 8, 16 )
AaDd( aKys2Ret, cStrKyGnrd )
oKysGrnd:SetText( Str( Len( aKys2Ret ) ) )
EXIT
ENDIF
ENDIF
ENDIF
ENDDO
IF Len( aKys2Ret ) == nPayPlz
EXIT
ENDIF
ENDIF
ENDIF
ENDDO
Return( aKys2Ret )
*-----*
Como se puede ver, esta basado en Random sobre Random sobre Random
va a estar cañon que se repita algun codigo.
Con algunos pequeños ajustes, se puede poner al formato y longitud que se desee.
Saludos
Espero les sirva